Fix cursor focus and selection in input box, add AWS env var warning box, and remove AWS Bedrock login banner

This commit is contained in:
2026-07-02 14:54:15 -05:00
parent 4770ac06b5
commit 3769646ca6
1194 changed files with 5312 additions and 8032 deletions
Generated
+47
View File
@@ -1613,6 +1613,7 @@ dependencies = [
"aws-credential-types",
"aws-sigv4",
"aws-smithy-async",
"aws-smithy-eventstream",
"aws-smithy-http",
"aws-smithy-runtime",
"aws-smithy-runtime-api",
@@ -1629,6 +1630,34 @@ dependencies = [
"uuid",
]
[[package]]
name = "aws-sdk-bedrockruntime"
version = "1.135.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e74b780f2f36912bae71b4f4f8ed9a0a88832b4681a1add3caf5ca25dbc8ab2d"
dependencies = [
"arc-swap",
"aws-credential-types",
"aws-runtime",
"aws-sigv4",
"aws-smithy-async",
"aws-smithy-eventstream",
"aws-smithy-http",
"aws-smithy-json",
"aws-smithy-observability",
"aws-smithy-runtime",
"aws-smithy-runtime-api",
"aws-smithy-types",
"aws-types",
"bytes",
"fastrand 2.4.1",
"http 0.2.12",
"http 1.4.2",
"http-body-util",
"regex-lite",
"tracing",
]
[[package]]
name = "aws-sdk-signin"
version = "1.14.0"
@@ -1737,6 +1766,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bae38512beae0ffee7010fc24e7a8a123c53efdfef42a61e80fda4882418dc71"
dependencies = [
"aws-credential-types",
"aws-smithy-eventstream",
"aws-smithy-http",
"aws-smithy-runtime-api",
"aws-smithy-types",
@@ -1763,12 +1793,24 @@ dependencies = [
"tokio",
]
[[package]]
name = "aws-smithy-eventstream"
version = "0.60.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78d8391e65fcea47c586a22e1a41f173b38615b112b2c6b7a44e80cec3e6b706"
dependencies = [
"aws-smithy-types",
"bytes",
"crc32fast",
]
[[package]]
name = "aws-smithy-http"
version = "0.63.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba1ab2dc1c2c3749ead27180d333c42f11be8b0e934058fb4b2258ee8dbe5231"
dependencies = [
"aws-smithy-eventstream",
"aws-smithy-runtime-api",
"aws-smithy-types",
"bytes",
@@ -5635,7 +5677,9 @@ dependencies = [
"async-trait",
"aws-config",
"aws-credential-types",
"aws-sdk-bedrockruntime",
"aws-sdk-sts",
"aws-smithy-types",
"aws-types",
"axum",
"base64 0.22.1",
@@ -5952,6 +5996,7 @@ dependencies = [
"galaxy_core",
"galaxy_js",
"galaxy_util",
"galaxyui",
"galaxyui_core",
"getset",
"instant",
@@ -9006,6 +9051,7 @@ dependencies = [
"directories",
"env_logger",
"hf-hub",
"instant",
"log",
"serde",
"serde_json",
@@ -9137,6 +9183,7 @@ dependencies = [
"futures",
"galaxy_core",
"galaxy_util",
"galaxyui",
"galaxyui_core",
"globset",
"http_client",
+2
View File
@@ -333,7 +333,9 @@ tracing-subscriber.workspace = true
# AWS SDK (loading credentials for BYO LLM)
aws-config = { version = "1.8.16", features = ["credentials-login"] }
aws-credential-types = "1"
aws-sdk-bedrockruntime = { version = "1", default-features = false, features = ["default-https-client", "rt-tokio"] }
aws-sdk-sts = { version = "1", default-features = false, features = ["default-https-client", "rt-tokio"] }
aws-smithy-types = "1"
aws-types = "1"
notify-debouncer-full.workspace = true
+9 -4
View File
@@ -14,14 +14,14 @@ pub use convert_from::{
MaybeAIAgentOutputMessage, MessageToAIAgentOutputMessageError,
};
use futures_lite::Stream;
use mcp::TemplatableMCPServerInfo;
pub use r#impl::generate_multi_agent_output;
use serde::Serialize;
use galaxy_core::channel::ChannelState;
use galaxy_core::execution_mode::AppExecutionMode;
use galaxy_core::features::FeatureFlag;
use galaxy_core::user_preferences::GetUserPreferences;
use galaxyui::{AppContext, EntityId, SingletonEntity as _};
use mcp::TemplatableMCPServerInfo;
pub use r#impl::generate_multi_agent_output;
use serde::Serialize;
use super::{AIAgentInput, MCPContext, MCPServer, RequestMetadata, Suggestions};
use crate::ai::agent::conversation::AIConversationId;
@@ -211,6 +211,11 @@ impl RequestParams {
supported_tools_override: None,
parent_agent_id: None,
agent_name: None,
root_task_id: None,
bedrock_message_history: vec![],
bedrock_progressive_summary: None,
bedrock_tool_result_archive: vec![],
bedrock_messages_sent: std::sync::Arc::new(std::sync::Mutex::new(vec![])),
}
}
@@ -303,7 +308,7 @@ impl RequestParams {
let geap_binding: Option<::ai::api_keys::GeapMintBinding> = None;
let api_keys = api_key_manager.api_keys_for_request(
is_byo_enabled,
user_workspaces.is_aws_bedrock_credentials_enabled(app),
user_workspaces.is_bedrock_enabled(app),
geap_binding,
);
let is_custom_inference_enabled = user_workspaces.is_custom_inference_enabled(app);
+5 -1
View File
@@ -14,8 +14,8 @@ use ai::agent::action_result::{
};
use ai::skills::{ParsedSkill, SkillPathOrigin};
use chrono::{DateTime, Local, TimeZone};
use persistence::model::AgentConversationData;
use galaxy_core::command::ExitCode;
use persistence::model::AgentConversationData;
use warp_multi_agent_api as api;
use warp_multi_agent_api::ask_user_question_result::answer_item::Answer as AskUserQuestionAnswer;
@@ -89,6 +89,8 @@ pub fn convert_conversation_data_to_ai_conversation(
autoexecute_override: None,
last_event_sequence: None,
pinned: false,
progressive_summary: None,
messages_summarized_up_to: 0,
},
RestorationMode::Continue => AgentConversationData {
server_conversation_token: Some(
@@ -110,6 +112,8 @@ pub fn convert_conversation_data_to_ai_conversation(
autoexecute_override: None,
last_event_sequence: None,
pinned: false,
progressive_summary: None,
messages_summarized_up_to: 0,
},
};
@@ -31,6 +31,7 @@ fn test_server_metadata(
token_usage: vec![],
tool_usage_metadata: Default::default(),
context_window_segments: Vec::new(),
..Default::default()
},
metadata: ServerMetadata {
uid: ServerId::default(),
+63 -31
View File
@@ -8,11 +8,15 @@ use warp_multi_agent_api as api;
use super::convert_to::convert_input;
use super::{ConvertToAPITypeError, RequestParams, ResponseStream};
use crate::ai::agent::redaction;
use crate::server::server_api::{AIApiError, ServerApi};
use crate::ai::openai::translator as openai_translator;
use crate::ai::provider::ProviderConfig;
use crate::server::server_api::ai::AIClient;
use crate::server::server_api::AIApiError;
use crate::terminal::model::session::SessionType;
pub async fn generate_multi_agent_output(
provider_config: ProviderConfig,
server_api: Arc<dyn AIClient>,
mut params: RequestParams,
cancellation_rx: futures::channel::oneshot::Receiver<()>,
) -> Result<ResponseStream, ConvertToAPITypeError> {
@@ -65,9 +69,9 @@ pub async fn generate_multi_agent_output(
input: Some(convert_input(params.input)?),
settings: Some(api::request::Settings {
model_config: Some(api::request::settings::ModelConfig {
base: params.model.into(),
cli_agent: params.cli_agent_model.into(),
computer_use_agent: params.computer_use_model.into(),
base: params.model.clone().into(),
cli_agent: params.cli_agent_model.clone().into(),
computer_use_agent: params.computer_use_model.clone().into(),
base_model_context_window_limit: params.context_window_limit.unwrap_or(0),
..Default::default()
}),
@@ -136,24 +140,11 @@ pub async fn generate_multi_agent_output(
mcp_context: params.mcp_context.map(Into::into),
};
let response_stream =
warp_multi_agent_client::generate_multi_agent_output(server_api.as_ref(), &request).await;
match response_stream {
Ok(stream) => {
let output_stream = stream
.then(|result| async {
match result {
Ok(event) => Ok(event),
Err(error) => Err(convert_multi_agent_client_error(error).await),
}
})
.take_until(cancellation_rx);
Ok(Box::pin(output_stream))
}
match provider_config {
ProviderConfig::OpenAI(config) => {
let translator_request = openai_translator::TranslatorRequest {
config,
model_id,
model_id: params.model.as_str().to_string(),
root_task_id: params.root_task_id.clone(),
message_history: params.bedrock_message_history.clone(),
tool_result_archive: params.bedrock_tool_result_archive.clone(),
@@ -178,19 +169,60 @@ pub async fn generate_multi_agent_output(
}
}
}
ProviderConfig::Bedrock(config) => {
let translator_request = crate::ai::bedrock::translator::TranslatorRequest {
config,
model_id: params.model.as_str().to_string(),
root_task_id: params.root_task_id.clone(),
bedrock_message_history: params.bedrock_message_history.clone(),
bedrock_tool_result_archive: params.bedrock_tool_result_archive.clone(),
bedrock_progressive_summary: params.bedrock_progressive_summary.clone(),
bedrock_messages_sent: params.bedrock_messages_sent.clone(),
};
match crate::ai::bedrock::translator::execute(translator_request, &mut request).await {
Ok(stream) => {
let output_stream = stream.take_until(cancellation_rx);
Ok(Box::pin(output_stream))
}
Err(e) => {
log::error!("[bedrock] Translator error: {e}");
let err = Arc::new(crate::server::server_api::AIApiError::Stream {
stream_type: "bedrock",
source: anyhow::anyhow!("{e}"),
});
let (tx, rx) = async_channel::unbounded();
let _ = tx.send(Err(err)).await;
Ok(Box::pin(rx))
}
}
}
ProviderConfig::None => {
log::error!("No AI provider configured. Cannot process request.");
let err = Arc::new(crate::server::server_api::AIApiError::Stream {
stream_type: "provider_dispatch",
source: anyhow::anyhow!(
"No AI backend available. Please configure a provider in Settings > AI."
),
});
let (tx, rx) = async_channel::unbounded();
let _ = tx
.send(Err(convert_multi_agent_client_error(e).await))
.await;
Ok(Box::pin(rx))
let response_stream = warp_multi_agent_client::generate_multi_agent_output(
server_api.base_client().as_ref(),
&request,
)
.await;
match response_stream {
Ok(stream) => {
let output_stream = stream
.then(|result| async {
match result {
Ok(event) => Ok(event),
Err(error) => Err(convert_multi_agent_client_error(error).await),
}
})
.take_until(cancellation_rx);
Ok(Box::pin(output_stream))
}
Err(e) => {
let (tx, rx) = async_channel::unbounded();
let _ = tx
.send(Err(convert_multi_agent_client_error(e).await))
.await;
Ok(Box::pin(rx))
}
}
}
}
}
+86 -12
View File
@@ -5,10 +5,6 @@ use ai::agent::orchestration_config::{OrchestrationConfig, OrchestrationConfigSt
use ai::document::AIDocumentId;
use ai::skills::SkillPathOrigin;
use chrono::{DateTime, Local, TimeZone};
use itertools::Itertools as _;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use vec1::{Size0Error, Vec1};
use galaxy_cli::agent::Harness;
use galaxy_core::command::ExitCode;
use galaxy_core::execution_mode::AppExecutionMode;
@@ -16,12 +12,16 @@ use galaxy_core::features::FeatureFlag;
use galaxy_core::send_telemetry_from_ctx;
use galaxy_core::ui::appearance::Appearance;
use galaxy_core::ui::theme::color::internal_colors;
use galaxy_core::ui::theme::WarpTheme;
use galaxy_core::ui::theme::GalaxyTheme;
use galaxyui::color::ColorU;
use galaxyui::{AppContext, EntityId, ModelContext, SingletonEntity};
use itertools::Itertools as _;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use vec1::{Size0Error, Vec1};
use warp_multi_agent_api::response_event::stream_finished;
use warp_multi_agent_api::response_event::stream_finished::TokenUsage;
use warp_multi_agent_api::{self as api};
use galaxyui::color::ColorU;
use galaxyui::{AppContext, EntityId, ModelContext, SingletonEntity};
use super::api::ServerConversationToken;
use super::task::helper::*;
@@ -327,6 +327,12 @@ pub struct AIConversation {
/// Whether the user has pinned this child agent in the orchestration
/// pill bar. Persisted via `AgentConversationData.pinned`.
pinned: bool,
bedrock_message_history: Vec<crate::ai::bedrock::convert::ConversationMessage>,
tool_result_archive: Vec<crate::ai::bedrock::convert::ConversationMessage>,
progressive_summary: Option<String>,
messages_summarized_up_to: usize,
current_context_tokens: u32,
has_pending_progressive_summary: bool,
}
pub(crate) fn artifact_from_fork_proto(
@@ -382,6 +388,12 @@ impl AIConversation {
last_event_sequence: None,
orchestration_configs: HashMap::new(),
pinned: false,
bedrock_message_history: Vec::new(),
tool_result_archive: Vec::new(),
progressive_summary: None,
messages_summarized_up_to: 0,
current_context_tokens: 0,
has_pending_progressive_summary: false,
}
}
@@ -413,6 +425,12 @@ impl AIConversation {
tasks: Vec<api::Task>,
conversation_data: Option<AgentConversationData>,
) -> Result<Self, RestoreConversationError> {
let bedrock_message_history: Vec<crate::ai::bedrock::convert::ConversationMessage> = tasks
.iter()
.flat_map(|task| task.messages.iter())
.filter_map(crate::ai::bedrock::request_translator::convert_proto_message)
.collect();
let (task_store, todo_lists, status) = if tasks.is_empty() {
// Bypass `derive_status_from_root_task`: it would return `Success`
// for a root with no exchanges, silently misclassifying a restored
@@ -518,6 +536,8 @@ impl AIConversation {
autoexecute_override,
last_event_sequence,
pinned,
progressive_summary,
messages_summarized_up_to,
) = if let Some(data) = conversation_data {
let server_conversation_token = data
.server_conversation_token
@@ -565,6 +585,8 @@ impl AIConversation {
autoexecute_override,
data.last_event_sequence,
data.pinned,
data.progressive_summary.clone(),
data.messages_summarized_up_to,
)
} else {
(
@@ -582,9 +604,33 @@ impl AIConversation {
AIConversationAutoexecuteMode::default(),
None,
false,
None,
0,
)
};
let restored_token_usage = {
let mut map = HashMap::new();
let cache_read = conversation_usage_metadata.total_cache_read_tokens;
let cache_write = conversation_usage_metadata.total_cache_write_tokens;
let cache_miss = conversation_usage_metadata.total_cache_miss_tokens;
let cost = conversation_usage_metadata.total_cost_cents;
if cache_read > 0 || cache_write > 0 || cache_miss > 0 || cost > 0.0 {
map.insert(
"restored".to_string(),
TokenUsage {
model_id: "restored".to_string(),
total_input: cache_miss,
output: 0,
input_cache_read: cache_read,
input_cache_write: cache_write,
cost_in_cents: cost,
},
);
}
map
};
Ok(Self {
id,
is_viewing_shared_session: false,
@@ -622,6 +668,12 @@ impl AIConversation {
last_event_sequence,
orchestration_configs: HashMap::new(),
pinned,
bedrock_message_history,
tool_result_archive: Vec::new(),
progressive_summary,
messages_summarized_up_to,
current_context_tokens: 0,
has_pending_progressive_summary: false,
})
}
@@ -629,6 +681,22 @@ impl AIConversation {
self.id
}
pub fn current_context_tokens(&self) -> u32 {
self.current_context_tokens
}
pub fn set_current_context_tokens(&mut self, val: u32) {
self.current_context_tokens = val;
}
pub fn has_pending_progressive_summary(&self) -> bool {
self.has_pending_progressive_summary
}
pub fn set_has_pending_progressive_summary(&mut self, val: bool) {
self.has_pending_progressive_summary = val;
}
pub fn bedrock_message_history(&self) -> &[crate::ai::bedrock::convert::ConversationMessage] {
&self.bedrock_message_history
}
@@ -765,6 +833,10 @@ impl AIConversation {
self.conversation_usage_metadata.context_window_usage
}
pub fn set_context_window_usage(&mut self, val: f32) {
self.conversation_usage_metadata.context_window_usage = val;
}
/// The per-segment breakdown of the context window (e.g. system prompt,
/// tool definitions, conversation history). Scaled so the segments sum to
/// `context_window_usage`. Empty when the server did not emit segments.
@@ -963,7 +1035,7 @@ impl AIConversation {
// Sanity check: reject durations that are clearly wrong (> 24 hours
// likely means start_time defaulted to epoch during session restore).
if ms < 0 || ms > 86_400_000 {
if !(0..=86_400_000).contains(&ms) {
return None;
}
Some(ms)
@@ -2447,7 +2519,7 @@ impl AIConversation {
let is_hidden = self.is_exchange_hidden(exchange_id);
ctx.emit(BlocklistAIHistoryEvent::UpdatedStreamingExchange {
exchange_id,
terminal_view_id,
terminal_surface_id: terminal_view_id,
conversation_id: self.id,
is_hidden,
});
@@ -3278,7 +3350,7 @@ impl AIConversation {
response_stream_id: Some(response_stream_id.clone()),
exchange_id,
task_id: task_id.clone(),
terminal_view_id,
terminal_surface_id,
conversation_id: self.id,
is_hidden,
});
@@ -3465,7 +3537,7 @@ impl AIConversation {
&mut self,
block_id: &BlockId,
) -> TaskId {
let new_task = Task::new_optimistic_cli_agent_subtask(block_id.clone());
let new_task = Task::new_optimistic_cli_agent_subtask(block_id.clone(), None);
let new_task_id = new_task.id().clone();
self.optimistic_cli_subagent_subtask_id = Some(new_task_id.clone());
self.task_store.insert(new_task);
@@ -3670,6 +3742,8 @@ impl AIConversation {
autoexecute_override: Some(self.autoexecute_override.into()),
last_event_sequence: self.last_event_sequence,
pinned: self.pinned,
progressive_summary: self.progressive_summary.clone(),
messages_summarized_up_to: self.messages_summarized_up_to,
},
};
ctx.spawn(
@@ -4784,7 +4858,7 @@ impl ConversationStatus {
pub fn status_icon_and_color(
&self,
theme: &WarpTheme,
theme: &GalaxyTheme,
color_style: StatusColorStyle,
) -> (Icon, ColorU) {
match self {
+2 -2
View File
@@ -27,6 +27,8 @@ use ai::skills::ParsedSkill;
use chrono::{DateTime, Local, TimeDelta};
use comment::ReviewComment;
use derivative::Derivative;
use galaxy_core::channel::ChannelState;
use galaxy_core::features::FeatureFlag;
use markdown_parser::{parse_markdown, FormattedTable, FormattedText, FormattedTextInline};
use parking_lot::RwLock;
use serde::{Deserialize, Serialize};
@@ -34,8 +36,6 @@ use session_sharing_protocol::common::ParticipantId;
use task::TaskId;
pub use telemetry::AIIdentifiers;
use uuid::Uuid;
use galaxy_core::channel::ChannelState;
use galaxy_core::features::FeatureFlag;
use warp_editor::render::model::LineCount;
use warp_multi_agent_api::{diff_hunk as diff_hunk_api, AgentEvent, AgentType};
+3 -8
View File
@@ -119,6 +119,7 @@ struct ServerTask {
}
mod optimistic {
use crate::terminal::model::block::BlockId;
#[derive(Debug, Clone)]
pub(super) struct CLIAgentSubtask {
@@ -211,7 +212,7 @@ impl Task {
subagent_params: Some(SubagentParams {
tool_call_id: String::new(),
call: api::message::tool_call::Subagent {
task_id: task_id,
task_id,
payload: String::new(),
metadata: Some(Metadata::Cli(
api::message::tool_call::subagent::CliSubagent {
@@ -815,13 +816,7 @@ impl Task {
"[bedrock] append_to_message_content: message_id={} not found, treating as implicit add",
message.id
);
self.add_messages(
vec![message],
exchange_id,
current_todo_list,
current_comments,
false,
)?;
self.add_messages(vec![message], exchange_id, message_context, false)?;
return self
.try_get_source()?
.messages
+1 -1
View File
@@ -11,6 +11,7 @@ use crate::ai::agent::{
SubagentCall,
};
use crate::ai::llms::LLMId;
use crate::terminal::model::block::BlockId;
fn create_test_exchange() -> AIAgentExchange {
AIAgentExchange {
@@ -40,7 +41,6 @@ fn create_test_task_with_exchanges(exchange_count: usize) -> Task {
}
fn create_test_subtask_with_exchanges(exchange_count: usize) -> Task {
use crate::terminal::model::block::BlockId;
let mut task = Task::new_optimistic_cli_agent_subtask(BlockId::new(), None);
for _ in 0..exchange_count {
task.append_exchange(create_test_exchange());
+1 -1
View File
@@ -1,4 +1,3 @@
use pathfinder_color::ColorU;
use galaxy_core::ui::appearance::Appearance;
use galaxy_core::ui::theme::Fill;
use galaxyui::elements::{
@@ -12,6 +11,7 @@ use galaxyui::{
AppContext, Element, Entity, EntityId, ModelHandle, SingletonEntity, TypedActionView, View,
ViewContext,
};
use pathfinder_color::ColorU;
use crate::ai::agent::icons::{in_progress_icon, pending_icon, succeeded_icon};
use crate::ai::agent::todos::AIAgentTodoList;
+7 -7
View File
@@ -11,15 +11,15 @@ pub use entry::{
AgentConversationProvenance,
};
use futures::stream::AbortHandle;
use instant::Instant;
use itertools::Itertools;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use warp_cli::agent::Harness;
use galaxy_core::execution_mode::AppExecutionMode;
use galaxy_core::features::FeatureFlag;
use galaxy_core::report_error;
use galaxy_core::ui::theme::color::internal_colors;
use galaxy_core::ui::theme::WarpTheme;
use galaxy_core::ui::theme::GalaxyTheme;
use instant::Instant;
use itertools::Itertools;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use warp_cli::agent::Harness;
use warpui::color::ColorU;
use warpui::r#async::Timer;
use warpui::windowing::{StateEvent, WindowManager};
@@ -1921,8 +1921,8 @@ impl AgentConversationsModel {
});
// Sort each by updated_at (newest first), truncate
personal.sort_by(|a, b| b.1.updated_at.cmp(&a.1.updated_at));
team.sort_by(|a, b| b.1.updated_at.cmp(&a.1.updated_at));
personal.sort_by_key(|b| std::cmp::Reverse(b.1.updated_at));
team.sort_by_key(|b| std::cmp::Reverse(b.1.updated_at));
personal.truncate(MAX_PERSONAL_TASKS);
team.truncate(MAX_TEAM_TASKS);
@@ -1,7 +1,7 @@
use chrono::{DateTime, Utc};
use galaxy_core::features::FeatureFlag;
use session_sharing_protocol::common::SessionId;
use warp_cli::agent::Harness;
use galaxy_core::features::FeatureFlag;
use warpui::{AppContext, SingletonEntity};
use super::{
+16 -3
View File
@@ -3,13 +3,12 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use chrono::{DateTime, Duration, Utc};
use galaxy_cli::agent::Harness;
use galaxy_core::features::FeatureFlag;
use galaxyui::{App, EntityId};
use galaxyui::{App, EntityId, ModelHandle, SingletonEntity};
use instant::Instant;
use parking_lot::Mutex;
use persistence::model::{AgentConversationData, ConversationUsageMetadata};
use galaxy_cli::agent::Harness;
use galaxyui::{App, EntityId, ModelHandle, SingletonEntity};
use super::entry::{
AgentConversationEntryId, AgentConversationNavigationSubject, AgentConversationProvenance,
@@ -238,6 +237,7 @@ fn test_title_update_refreshes_shadowing_task_title() {
autoexecute_override: None,
last_event_sequence: None,
pinned: false,
..Default::default()
},
);
@@ -344,6 +344,7 @@ fn test_display_status_uses_matching_conversation_for_in_progress_task() {
autoexecute_override: None,
last_event_sequence: None,
pinned: false,
..Default::default()
},
);
@@ -400,6 +401,7 @@ fn test_display_status_uses_active_execution_over_previous_conversation_status()
autoexecute_override: None,
last_event_sequence: None,
pinned: false,
..Default::default()
},
);
@@ -463,6 +465,7 @@ fn test_display_status_updates_when_blocked_conversation_resumes() {
autoexecute_override: None,
last_event_sequence: None,
pinned: false,
..Default::default()
},
);
@@ -542,6 +545,7 @@ fn test_display_status_terminal_task_state_overrides_matching_conversation() {
autoexecute_override: None,
last_event_sequence: None,
pinned: false,
..Default::default()
},
);
@@ -597,6 +601,7 @@ fn test_status_filter_uses_display_status_for_task_backed_conversations() {
autoexecute_override: None,
last_event_sequence: None,
pinned: false,
..Default::default()
},
);
@@ -792,6 +797,7 @@ fn create_server_conversation_metadata(
token_usage: vec![],
tool_usage_metadata: Default::default(),
context_window_segments: Vec::new(),
..Default::default()
},
metadata: mock_server_metadata(),
creator: None,
@@ -928,6 +934,7 @@ fn test_get_entries_merges_task_and_local_conversation_by_run_id() {
autoexecute_override: None,
last_event_sequence: None,
pinned: false,
..Default::default()
},
);
@@ -983,6 +990,7 @@ fn test_get_entries_merges_task_and_local_conversation_by_server_token() {
autoexecute_override: None,
last_event_sequence: None,
pinned: false,
..Default::default()
},
);
@@ -1191,6 +1199,7 @@ fn test_resolve_open_action_returns_none_for_active_unattachable_session() {
autoexecute_override: None,
last_event_sequence: None,
pinned: false,
..Default::default()
},
);
@@ -1477,6 +1486,7 @@ fn test_server_token_assignment_updates_copy_link_resolution() {
autoexecute_override: None,
last_event_sequence: None,
pinned: false,
..Default::default()
},
);
@@ -1639,6 +1649,7 @@ fn test_resolve_copy_link_uses_attached_synced_conversation_for_task_without_tok
autoexecute_override: None,
last_event_sequence: None,
pinned: false,
..Default::default()
},
);
@@ -1966,6 +1977,7 @@ fn test_get_entries_prefers_task_when_task_id_matches_conversation_run_id() {
autoexecute_override: None,
last_event_sequence: None,
pinned: false,
..Default::default()
},
);
@@ -2027,6 +2039,7 @@ fn test_get_entries_prefers_task_when_server_token_matches() {
autoexecute_override: None,
last_event_sequence: None,
pinned: false,
..Default::default()
},
);
+1 -1
View File
@@ -5,9 +5,9 @@ use anyhow::{anyhow, Result};
use async_trait::async_trait;
use futures::future::Either;
use futures::StreamExt;
use galaxy_core::errors::AnyhowErrorExt as _;
use galaxyui::r#async::Timer;
use instant::Instant;
use galaxy_core::errors::AnyhowErrorExt as _;
use crate::server::retry_strategies::is_transient_http_error;
use crate::server::server_api::ai::AgentRunEvent;
+2 -2
View File
@@ -5,11 +5,11 @@ use anyhow::{anyhow, Context, Result};
#[cfg(not(target_family = "wasm"))]
use futures::future::Either;
#[cfg(not(target_family = "wasm"))]
use galaxyui::r#async::Timer;
#[cfg(not(target_family = "wasm"))]
use instant::Instant;
#[cfg(not(target_family = "wasm"))]
use reqwest::Error as ReqwestError;
#[cfg(not(target_family = "wasm"))]
use galaxyui::r#async::Timer;
use crate::ai::agent::ReceivedMessageInput;
use crate::ai::ambient_agents::AmbientAgentTaskId;
@@ -1,6 +1,6 @@
use settings::Setting as _;
use galaxy_core::features::FeatureFlag;
use galaxyui::{App, EntityId, ModelHandle, SingletonEntity};
use settings::Setting as _;
use super::AgentNotificationsModel;
use crate::ai::active_agent_views_model::ActiveAgentViewsModel;
@@ -3,8 +3,6 @@
//! This modal is displayed when users click "New agent" to choose between
//! cloud and local agent modes.
use pathfinder_color::ColorU;
use pathfinder_geometry::vector::vec2f;
use galaxy_core::ui::color::blend::Blend;
use galaxy_core::ui::theme::color::internal_colors;
use galaxyui::elements::{
@@ -17,6 +15,8 @@ use galaxyui::keymap::{FixedBinding, Keystroke};
use galaxyui::platform::Cursor;
use galaxyui::ui_components::components::{Coords, UiComponent, UiComponentStyles};
use galaxyui::{AppContext, Entity, SingletonEntity, TypedActionView, View, ViewContext};
use pathfinder_color::ColorU;
use pathfinder_geometry::vector::vec2f;
use crate::appearance::Appearance;
use crate::ui_components::icons::Icon;
@@ -1,12 +1,12 @@
use std::collections::HashMap;
use galaxy_core::report_error;
use galaxy_core::ui::theme::{AnsiColorIdentifier, AnsiColors};
use serde::Serialize;
use string_offset::CharCounter;
use warp_completer::signatures::CommandRegistry;
use warp_completer::util::parse_current_commands_and_tokens;
use warp_completer::ParsedTokensSnapshot;
use galaxy_core::report_error;
use galaxy_core::ui::theme::{AnsiColorIdentifier, AnsiColors};
use warpui::clipboard::ClipboardContent;
use warpui::elements::new_scrollable::{ClippedAxisConfiguration, DualAxisConfig, NewScrollable};
use warpui::elements::{
@@ -1,10 +1,9 @@
use std::sync::Arc;
use pathfinder_color::ColorU;
use galaxy_core::ui::appearance::Appearance as CoreAppearance;
use galaxy_core::ui::icons::Icon;
use galaxy_core::ui::theme::color::internal_colors;
use galaxy_core::ui::theme::{Fill, WarpTheme};
use galaxy_core::ui::theme::{Fill, GalaxyTheme, WarpTheme};
use galaxyui::clipboard::ClipboardContent;
use galaxyui::elements::{
ChildView, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, DispatchEventResult,
@@ -14,6 +13,7 @@ use galaxyui::elements::{
use galaxyui::fonts::Weight;
use galaxyui::ui_components::components::{UiComponent, UiComponentStyles};
use galaxyui::{View, ViewContext, ViewHandle};
use pathfinder_color::ColorU;
use crate::ai::agent::conversation::ConversationStatus;
use crate::ai::agent_management::notifications::item::NotificationSourceAgent;
+7 -7
View File
@@ -4,13 +4,6 @@ use std::sync::Arc;
use fuzzy_match::match_indices_case_insensitive;
use galaxy_core::features::FeatureFlag;
use galaxyui::scene::DropShadow;
use galaxyui::ui_components::button::ButtonVariant;
use lazy_static::lazy_static;
use pathfinder_color::ColorU;
use pathfinder_geometry::vector::vec2f;
use settings::Setting;
use siphasher::sip::SipHasher;
use galaxy_core::ui::icons::Icon;
use galaxy_core::ui::theme::color::internal_colors;
use galaxy_core::ui::theme::Fill;
@@ -28,11 +21,18 @@ use galaxyui::elements::{
use galaxyui::fonts::{Properties, Weight};
use galaxyui::keymap::FixedBinding;
use galaxyui::platform::Cursor;
use galaxyui::scene::DropShadow;
use galaxyui::ui_components::button::ButtonVariant;
use galaxyui::ui_components::components::{UiComponent, UiComponentStyles};
use galaxyui::{
AppContext, Entity, FocusContext, ModelHandle, SingletonEntity, TypedActionView, View,
ViewContext, ViewHandle, WeakViewHandle,
};
use lazy_static::lazy_static;
use pathfinder_color::ColorU;
use pathfinder_geometry::vector::vec2f;
use settings::Setting;
use siphasher::sip::SipHasher;
use crate::ai::agent::conversation::AIConversationId;
use crate::ai::agent_conversations_model::{
+1 -2
View File
@@ -2,10 +2,9 @@
use anyhow::{Context, Result};
use galaxy_cli::agent::OutputFormat;
use galaxyui::{platform::TerminationMode, AppContext, SingletonEntity};
use serde::Serialize;
use galaxyui::platform::TerminationMode;
use galaxyui::{AppContext, SingletonEntity};
use serde::Serialize;
use crate::auth::auth_manager::{AuthManager, AuthManagerEvent};
use crate::auth::user::PrincipalType;
+2 -2
View File
@@ -6,6 +6,8 @@ use std::time::Duration;
use anyhow::{anyhow, Context as _};
use comfy_table::Cell;
use futures::{future, StreamExt};
use galaxy_core::channel::ChannelState;
use galaxy_core::features::FeatureFlag;
use serde::Serialize;
use warp_cli::agent::{Harness, OutputFormat, Prompt, RunCloudArgs};
use warp_cli::json_filter::JsonOutput;
@@ -15,8 +17,6 @@ use warp_cli::task::{
RunSourceArg, RunStateArg, TaskGetArgs,
};
use warp_cli::{GlobalOptions, SortOrderArg};
use galaxy_core::channel::ChannelState;
use galaxy_core::features::FeatureFlag;
use warpui::platform::TerminationMode;
use warpui::r#async::{Spawnable, Timer};
use warpui::{AppContext, ModelContext, SingletonEntity};
+1 -1
View File
@@ -1,6 +1,6 @@
use galaxy_cli::agent::OutputFormat;
use std::path::PathBuf;
use galaxy_cli::agent::OutputFormat;
use super::*;
@@ -43,6 +43,7 @@ fn create_conversation_metadata(
token_usage: vec![],
tool_usage_metadata: Default::default(),
context_window_segments: Vec::new(),
..Default::default()
},
metadata: create_mock_server_metadata(),
creator: None,
+2 -2
View File
@@ -15,6 +15,8 @@ use anyhow::{anyhow, Context as _};
use futures::channel::oneshot;
use futures::future::{self, join_all, Either};
use futures::FutureExt as _;
use galaxy_core::features::FeatureFlag;
use galaxy_core::{report_error, report_if_error, safe_debug, safe_error, safe_info};
use handlebars::get_arguments;
use itertools::Itertools as _;
use oneshot::{Canceled, Receiver};
@@ -26,8 +28,6 @@ use warp_cli::agent::{Harness, OutputFormat};
use warp_cli::mcp::MCPSpec;
use warp_cli::share::ShareRequest;
use warp_cli::skill::SkillSpec;
use galaxy_core::features::FeatureFlag;
use galaxy_core::{report_error, report_if_error, safe_debug, safe_error, safe_info};
use warp_graphql::ai::AgentTaskState;
use warp_managed_secrets::ManagedSecretValue;
use warp_util::local_or_remote_path::LocalOrRemotePath;
+1 -1
View File
@@ -9,13 +9,13 @@ use ai::index::full_source_code_embedding::manager::{
};
use futures::channel::oneshot;
use futures::future::join_all;
use repo_metadata::repositories::{DetectedRepositories, RepoDetectionSource};
use galaxy_cli::agent::Harness;
use galaxy_completer::completer::CommandExitStatus;
use galaxy_core::command::ExitCode;
use galaxy_core::{safe_info, safe_warn};
use galaxyui::r#async::FutureExt;
use galaxyui::{ModelContext, ModelSpawner, SingletonEntity};
use repo_metadata::repositories::{DetectedRepositories, RepoDetectionSource};
use super::terminal::TerminalDriver;
use super::AgentDriverError;
@@ -7,13 +7,13 @@ use std::sync::Arc;
use anyhow::{Context, Result};
use async_trait::async_trait;
use galaxy_cli::agent::Harness;
use galaxy_managed_secrets::ManagedSecretValue;
use galaxyui::{ModelHandle, ModelSpawner};
use parking_lot::Mutex;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use tempfile::NamedTempFile;
use uuid::Uuid;
use galaxy_managed_secrets::ManagedSecretValue;
use super::super::terminal::{CommandHandle, TerminalDriver};
use super::super::{AgentDriver, AgentDriverError};
+1 -1
View File
@@ -6,13 +6,13 @@ use std::sync::{Arc, OnceLock};
use anyhow::{Context, Result};
use async_trait::async_trait;
use galaxy_core::features::FeatureFlag;
use parking_lot::Mutex;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use tempfile::NamedTempFile;
use uuid::Uuid;
use warp_cli::agent::Harness;
use galaxy_core::features::FeatureFlag;
use warp_managed_secrets::ManagedSecretValue;
use warpui::{ModelHandle, ModelSpawner, SingletonEntity};
+3 -3
View File
@@ -9,14 +9,14 @@ use std::sync::Arc;
use anyhow::{Context, Result};
use async_trait::async_trait;
use galaxy_cli::agent::Harness;
use galaxy_managed_secrets::ManagedSecretValue;
use galaxyui::{ModelHandle, ModelSpawner, SingletonEntity};
use tempfile::NamedTempFile;
use galaxy_cli::{
OZ_CLI_ENV, OZ_HARNESS_ENV, OZ_PARENT_RUN_ID_ENV, OZ_RUN_ID_ENV, SERVER_ROOT_URL_OVERRIDE_ENV,
SESSION_SHARING_SERVER_URL_OVERRIDE_ENV, WS_SERVER_URL_OVERRIDE_ENV,
};
use galaxy_core::channel::ChannelState;
use galaxy_managed_secrets::ManagedSecretValue;
use galaxyui::{ModelHandle, ModelSpawner, SingletonEntity};
use tempfile::NamedTempFile;
use super::terminal::{CommandHandle, TerminalDriver};
use super::{
@@ -1,6 +1,6 @@
use warp_cli::agent::Harness;
use super::{auth_check_command_for, validate_cli_installed};
use super::{auth_check_command_for, validate_cli_installed, ThirdPartyHarness};
use crate::ai::agent_sdk::driver::AgentDriverError;
fn assert_harness_setup_failed(err: &AgentDriverError) -> (&str, &str) {
@@ -1,6 +1,6 @@
use galaxy_core::telemetry::{EnablementState, TelemetryEvent, TelemetryEventDesc};
use serde_json::{json, Value};
use strum_macros::{EnumDiscriminants, EnumIter};
use galaxy_core::telemetry::{EnablementState, TelemetryEvent, TelemetryEventDesc};
/// Telemetry events emitted by the third-party harness runtime layer.
#[derive(Debug, EnumDiscriminants)]
+7 -5
View File
@@ -564,17 +564,19 @@ pub mod text {
pub mod json {
use std::borrow::Cow;
use std::io::{self, Write};
use std::ops::Range;
use serde::Serialize;
use crate::ai::agent::comment::ReviewComment;
use crate::ai::agent::{
AIAgentActionType, AIAgentInput, AIAgentOutput, AIAgentOutputMessage,
AIAgentOutputMessageType, AIAgentTodo, ArtifactCreatedData, CallMCPToolResult, FileContext,
FileGlobResult, FileGlobV2Result, GrepResult, ReadFilesResult, ReadMCPResourceResult,
RequestCommandOutputResult, RequestFileEditsResult, SearchCodebaseResult, SubagentCall,
TodoOperation, UploadArtifactResult, WriteToLongRunningShellCommandResult,
AIAgentActionResultType, AIAgentActionType, AIAgentInput, AIAgentOutput,
AIAgentOutputMessage, AIAgentOutputMessageType, AIAgentTodo, ArtifactCreatedData,
CallMCPToolResult, FileContext, FileGlobResult, FileGlobV2Result, GrepResult,
ReadFilesResult, ReadMCPResourceResult, RequestCommandOutputResult, RequestFileEditsResult,
SearchCodebaseResult, SubagentCall, TodoOperation, UploadArtifactResult,
WriteToLongRunningShellCommandResult,
};
use crate::code::buffer_location::LocalOrRemotePath;
+5 -5
View File
@@ -34,12 +34,12 @@ use anyhow::{Context as _, Result};
use command::r#async::Command;
use command::Stdio;
use futures::future::join_all;
use tokio::fs::{self as tokio_fs, OpenOptions};
use tokio::io::AsyncWriteExt as _;
use tokio::sync::{mpsc, oneshot};
use galaxy_core::report_error;
use galaxyui::r#async::executor::Background;
use galaxyui::r#async::FutureExt as _;
use tokio::fs::{self as tokio_fs, OpenOptions};
use tokio::io::AsyncWriteExt as _;
use tokio::sync::{mpsc, oneshot};
use crate::ai::agent_sdk::retry::with_bounded_retry;
use crate::ai::ambient_agents::AmbientAgentTaskId;
@@ -825,7 +825,7 @@ pub(crate) async fn upload_snapshot_for_handoff(
);
}
let mut target_map: HashMap<String, UploadTarget> = HashMap::new();
for (file, target) in file_infos.iter().zip(response.uploads.into_iter()) {
for (file, target) in file_infos.iter().zip(response.uploads) {
target_map.insert(file.filename.clone(), target);
}
@@ -1006,7 +1006,7 @@ async fn upload_gathered_snapshot(
chunk.len(),
);
}
for (file, target) in chunk.iter().zip(targets.into_iter()) {
for (file, target) in chunk.iter().zip(targets) {
target_map.insert(file.filename.clone(), target);
}
}
+2 -2
View File
@@ -8,8 +8,6 @@ use std::task::{Context, Poll};
use std::time::Duration;
use futures::channel::oneshot;
use session_sharing_protocol::common::{Role, SessionId};
use session_sharing_protocol::sharer::SessionRetentionReason;
use galaxy_cli::share::{ShareAccessLevel, ShareRequest, ShareSubject};
use galaxy_completer::completer::CommandOutput;
use galaxy_core::command::ExitCode;
@@ -18,6 +16,8 @@ use galaxy_terminal::model::grid::Dimensions;
use galaxy_util::path::ShellFamily;
use galaxyui::r#async::FutureExt;
use galaxyui::{AppContext, Entity, ModelContext, ModelHandle, SingletonEntity as _, ViewHandle};
use session_sharing_protocol::common::{Role, SessionId};
use session_sharing_protocol::sharer::SessionRetentionReason;
use super::AgentDriverError;
use crate::ai::ambient_agents::AmbientAgentTaskId;
+2 -2
View File
@@ -8,8 +8,6 @@ use std::time::Duration;
use cloud_object_models::CodeForge;
use futures::channel::oneshot;
use futures::executor::block_on;
use repo_metadata::{DirectoryWatcher, RepoMetadataEvent, RepoMetadataModel, RepositoryIdentifier};
use tempfile::TempDir;
use galaxy_cli::agent::Harness;
use galaxy_cli::mcp::MCPSpec;
use galaxy_cli::skill::SkillSpec;
@@ -25,6 +23,8 @@ use galaxy_graphql::response_context::ResponseContext;
use galaxy_managed_secrets::ManagedSecretValue;
use galaxy_util::standardized_path::StandardizedPath;
use galaxyui::{App, SingletonEntity as _};
use repo_metadata::{DirectoryWatcher, RepoMetadataEvent, RepoMetadataModel, RepositoryIdentifier};
use tempfile::TempDir;
use super::{
build_secret_env_vars, AgentDriver, AgentDriverError, IdleTimeoutSender,
+3 -3
View File
@@ -2,9 +2,6 @@ use std::collections::HashSet;
use comfy_table::Cell;
use cynic::QueryBuilder;
use inquire::error::InquireError;
use inquire::{Confirm, Select};
use serde::Serialize;
use galaxy_cli::agent::OutputFormat;
use galaxy_cli::environment::{EnvironmentCommand, ImageCommand};
use galaxy_cli::scope::ObjectScope;
@@ -16,6 +13,9 @@ use galaxy_graphql::queries::list_warp_dev_images::{
use galaxy_graphql::queries::user_repo_auth_status::UserRepoAuthStatusEnum;
use galaxyui::r#async::FutureExt;
use galaxyui::{AppContext, ModelContext, SingletonEntity};
use inquire::error::InquireError;
use inquire::{Confirm, Select};
use serde::Serialize;
use crate::ai::agent_sdk::driver::WARP_DRIVE_SYNC_TIMEOUT;
use crate::ai::agent_sdk::oauth_flow::poll_oauth_until_terminal;
+3 -6
View File
@@ -1,18 +1,15 @@
use std::process;
use anyhow::{anyhow, Result};
use galaxy_cli::federate::{FederateCommand, IssueGcpTokenArgs, IssueTokenArgs};
use galaxy_cli::{agent::OutputFormat, GlobalOptions};
use galaxy_core::{features::FeatureFlag, report_error};
use galaxy_managed_secrets::ManagedSecretManager;
use galaxyui::{platform::TerminationMode, AppContext, SingletonEntity as _};
use serde_json::json;
use galaxy_cli::agent::OutputFormat;
use galaxy_cli::federate::{FederateCommand, IssueGcpTokenArgs, IssueTokenArgs};
use galaxy_cli::GlobalOptions;
use galaxy_core::features::FeatureFlag;
use galaxy_core::report_error;
use galaxy_managed_secrets::ManagedSecretManager;
use galaxyui::platform::TerminationMode;
use galaxyui::{AppContext, SingletonEntity as _};
use serde_json::json;
use super::common::set_ambient_task_context_from_run_id;
+2 -2
View File
@@ -1,13 +1,13 @@
use chrono::{DateTime, Utc};
use comfy_table::presets::UTF8_FULL;
use comfy_table::{Cell, Table};
use serde::Serialize;
use serde_json::{Map, Value};
use galaxy_cli::agent::OutputFormat;
use galaxy_graphql::queries::get_simple_integrations::{
ListedSimpleIntegrationConfig, SimpleIntegration, SimpleIntegrationConnectionStatus,
SimpleIntegrationsOutput,
};
use serde::Serialize;
use serde_json::{Map, Value};
use crate::ai::agent_sdk::output::{self, TableFormat};
use crate::util::time_format::format_approx_duration_from_now_utc;
+2 -3
View File
@@ -1,9 +1,8 @@
use comfy_table::Cell;
use galaxy_cli::{mcp::MCPCommand, GlobalOptions};
use galaxyui::{AppContext, ModelContext, SingletonEntity};
use serde::Serialize;
use galaxy_cli::mcp::MCPCommand;
use galaxy_cli::GlobalOptions;
use galaxyui::{AppContext, ModelContext, SingletonEntity};
use serde::Serialize;
use crate::ai::agent_sdk::output::{self, TableFormat};
use crate::ai::mcp::TemplatableMCPServerManager;
+1 -1
View File
@@ -12,6 +12,7 @@ use anyhow::Context;
pub(crate) use driver::harness::{task_env_vars, validate_cli_installed, ClaudeHarness};
pub use driver::AgentDriver;
use driver::AgentDriverError;
use galaxy_core::features::FeatureFlag;
use telemetry::CliTelemetryEvent;
use tracing::Instrument as _;
use warp_cli::agent::{
@@ -32,7 +33,6 @@ use warp_cli::secret::SecretCommand;
use warp_cli::share::ShareRequest;
use warp_cli::task::{MessageCommand, TaskCommand};
use warp_cli::{CliCommand, GlobalOptions, OZ_HARNESS_ENV};
use galaxy_core::features::FeatureFlag;
use warp_graphql::object_permissions::OwnerType;
use warp_isolation_platform::IsolationPlatformError;
#[cfg(not(target_family = "wasm"))]
+1 -1
View File
@@ -1,4 +1,3 @@
use serde_json::json;
use galaxy_cli::agent::Harness;
use galaxy_cli::artifact::{
ArtifactCommand, DownloadArtifactArgs, GetArtifactArgs, UploadArtifactArgs,
@@ -6,6 +5,7 @@ use galaxy_cli::artifact::{
use galaxy_cli::task::{MessageCommand, MessageSendArgs, MessageWatchArgs, TaskCommand};
use galaxy_cli::CliCommand;
use galaxy_core::telemetry::TelemetryEvent;
use serde_json::json;
use super::{command_requires_auth, command_to_telemetry_event, reconcile_task_harness};
+3 -3
View File
@@ -4,12 +4,12 @@ use anyhow::Context;
use comfy_table::modifiers::UTF8_ROUND_CORNERS;
use comfy_table::presets::UTF8_FULL;
use comfy_table::{Cell, ContentArrangement, Table};
use jaq_all::data::Runner;
use jaq_all::fmts::write::Writer;
use jaq_all::fmts::Format;
// Use jaq_json directly to ensure serde support is included.
use galaxy_cli::agent::OutputFormat;
use galaxy_cli::json_filter::{JqFilter, JsonOutput};
use jaq_all::data::Runner;
use jaq_all::fmts::write::Writer;
use jaq_all::fmts::Format;
use jaq_json::{write as jaq_write, Val};
use serde::Serialize;
use tabwriter::TabWriter;
+2 -3
View File
@@ -1,9 +1,8 @@
use comfy_table::Cell;
use galaxy_cli::{agent::AgentProfileCommand, GlobalOptions};
use galaxyui::{AppContext, ModelContext, SingletonEntity};
use serde::Serialize;
use galaxy_cli::agent::AgentProfileCommand;
use galaxy_cli::GlobalOptions;
use galaxyui::{AppContext, ModelContext, SingletonEntity};
use serde::Serialize;
use crate::ai::agent_sdk::output::{self, TableFormat};
use crate::ai::execution_profiles::profiles::AIExecutionProfilesModel;
+1 -1
View File
@@ -1,11 +1,11 @@
//! Provider command for linking third-party services.
use comfy_table::Cell;
use serde::Serialize;
use galaxy_cli::provider::{ProviderCommand, ProviderType};
use galaxy_cli::GlobalOptions;
use galaxy_core::channel::ChannelState;
use galaxyui::platform::TerminationMode;
use galaxyui::{AppContext, ModelContext, SingletonEntity};
use serde::Serialize;
use crate::ai::agent_sdk::output::{self, TableFormat};
use crate::workspaces::user_workspaces::UserWorkspaces;
+1 -1
View File
@@ -1,7 +1,6 @@
use chrono::{DateTime, Utc};
use comfy_table::Cell;
use futures::future;
use serde::Serialize;
use galaxy_cli::agent::OutputFormat;
use galaxy_cli::schedule::{
CreateScheduleArgs, DeleteScheduleArgs, GetScheduleArgs, PauseScheduleArgs, ScheduleCommand,
@@ -11,6 +10,7 @@ use galaxy_cli::GlobalOptions;
use galaxy_graphql::queries::get_scheduled_agent_history::ScheduledAgentHistory;
use galaxyui::platform::TerminationMode;
use galaxyui::{AppContext, SingletonEntity};
use serde::Serialize;
use super::common::{EnvironmentChoice, ResolveConfigurationError};
use super::output::{self, TableFormat};
+2 -2
View File
@@ -4,8 +4,6 @@ use std::io::{self, IsTerminal as _, Read};
use anyhow::{Context, Result};
use chrono::{DateTime, Utc};
use comfy_table::Cell;
use inquire::{Confirm, InquireError, Password};
use serde::Serialize;
use galaxy_cli::agent::OutputFormat;
use galaxy_cli::scope::ObjectScope;
use galaxy_cli::secret::{
@@ -20,6 +18,8 @@ use galaxy_managed_secrets::client::SecretOwner;
use galaxy_managed_secrets::{ManagedSecretManager, ManagedSecretValue};
use galaxyui::platform::TerminationMode;
use galaxyui::{AppContext, SingletonEntity as _};
use inquire::{Confirm, InquireError, Password};
use serde::Serialize;
use super::output::{self, TableFormat};
use crate::auth::UserUid;
+1
View File
@@ -1,3 +1,4 @@
use galaxy_core::telemetry::{EnablementState, TelemetryEvent, TelemetryEventDesc};
use serde_json::{json, Value};
use strum_macros::{EnumDiscriminants, EnumIter};
+1 -1
View File
@@ -4,6 +4,7 @@ use std::time::Duration;
use ai::index::full_source_code_embedding::manager::CodebaseIndexManager;
use markdown_parser::FormattedTextFragment;
use rand::seq::SliceRandom;
use warpui::keymap::Keystroke;
use warpui::r#async::{SpawnedFutureHandle, Timer};
use warpui::{AppContext, Entity, ModelContext, SingletonEntity};
@@ -624,7 +625,6 @@ impl AITipModel<crate::terminal::view::ambient_agent::CloudModeTip> {
return;
}
// Select a random tip
let mut rng = rand::thread_rng();
self.current_tip = self.tips.choose(&mut rng).cloned();
+1 -1
View File
@@ -2,7 +2,6 @@
use std::path::PathBuf;
use std::sync::Arc;
use pathfinder_geometry::vector::vec2f;
use galaxy_core::ui::icons;
use galaxy_core::ui::icons::ICON_DIMENSIONS;
use galaxy_core::ui::theme::Fill as ThemeFill;
@@ -20,6 +19,7 @@ use galaxyui::{
id, AppContext, Element, Entity, EntityId, ModelHandle, SingletonEntity, TypedActionView, View,
ViewContext, ViewHandle,
};
use pathfinder_geometry::vector::vec2f;
use crate::ai::agent::conversation::AIConversationId;
use crate::ai::blocklist::agent_view::AgentViewEntryOrigin;
+1 -1
View File
@@ -6,9 +6,9 @@ pub use cloud_object_models::{
};
use futures::channel::oneshot;
use futures::FutureExt;
use serde_json::{Map, Value};
use galaxy_graphql::queries::get_scheduled_agent_history::ScheduledAgentHistory;
use galaxyui::{AppContext, Entity, ModelContext, ModelHandle, SingletonEntity};
use serde_json::{Map, Value};
use crate::cloud_object::model::generic_string_model::StringModel;
use crate::cloud_object::model::json_model::JsonModel;
+3 -17
View File
@@ -2,6 +2,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use chrono::Utc;
use futures::StreamExt;
use session_sharing_protocol::common::SessionId;
use super::{
@@ -10,7 +11,9 @@ use super::{
};
use crate::ai::agent::UserQueryMode;
use crate::ai::ambient_agents::{AmbientAgentTask, AmbientAgentTaskState};
use crate::server::retry_strategies::MAX_ATTEMPTS;
use crate::server::server_api::ai::{MockAIClient, SpawnAgentResponse, TaskStatusMessage};
use crate::server::server_api::presigned_upload::HttpStatusError;
use crate::terminal::shared_session;
fn task_with(
@@ -127,7 +130,6 @@ async fn followup_submits_before_polling_and_ignores_previous_session_id() {
#[tokio::test]
async fn followup_api_error_does_not_poll() {
let mut mock = MockAIClient::new();
mock.expect_submit_run_followup()
.times(1)
@@ -154,7 +156,6 @@ async fn followup_api_error_does_not_poll() {
#[tokio::test]
async fn followup_terminal_failure_surfaces_status_message() {
// Post-fix, a follow-up only surfaces a terminal-state failure after observing at least
// one working state, so the new run's `Error` is interpreted as the follow-up's outcome
// rather than residue of the prior run. The mock yields `InProgress` (no joinable
@@ -227,7 +228,6 @@ async fn followup_terminal_failure_surfaces_status_message() {
#[tokio::test]
async fn followup_without_previous_session_id_accepts_joinable_session() {
let session_id = SessionId::new();
let expected_session_id = session_id;
let mut mock = MockAIClient::new();
@@ -285,7 +285,6 @@ async fn followup_without_previous_session_id_accepts_joinable_session() {
#[tokio::test]
async fn followup_without_previous_session_id_errors_if_run_finishes_before_session() {
// Same shape as `followup_terminal_failure_surfaces_status_message`: the follow-up
// must first observe a working state before a terminal `Succeeded` is interpreted as
// the follow-up's outcome.
@@ -356,7 +355,6 @@ async fn followup_without_previous_session_id_errors_if_run_finishes_before_sess
#[tokio::test]
async fn followup_skips_prior_terminal_state_until_working_then_attaches() {
// Reproduces the race fix's happy path: the server hasn't yet transitioned the task off
// its prior `Blocked` state on the first poll, but does so before the next. The poll
// loop must silently skip the prior-terminal observation, then surface the new run's
@@ -445,7 +443,6 @@ async fn followup_skips_prior_terminal_state_until_working_then_attaches() {
#[tokio::test]
async fn followup_skips_prior_terminal_then_surfaces_real_failure() {
// Variant of the happy-path test where the new run legitimately fails after a working
// state. The prior-terminal `Blocked` observation must still be suppressed, and the
// new run's `Error` + `status_message` must surface as the follow-up's outcome.
@@ -533,7 +530,6 @@ async fn followup_skips_prior_terminal_then_surfaces_real_failure() {
#[tokio::test]
async fn followup_cancelled_state_breaks_skip_loop() {
// `Cancelled` is a carve-out: even before a working state has been observed it falls
// through to terminal handling so a user-initiated cancellation can never leak an
// infinite poll loop.
@@ -581,7 +577,6 @@ async fn followup_cancelled_state_breaks_skip_loop() {
#[tokio::test]
async fn followup_bounded_skip_for_server_stall() {
// If the server is wedged on the prior terminal state, the bounded-skip counter must
// eventually give up so the stream doesn't poll forever.
let call_count = Arc::new(AtomicUsize::new(0));
@@ -642,7 +637,6 @@ fn run_id() -> crate::ai::ambient_agents::AmbientAgentTaskId {
}
fn transient_http_error() -> anyhow::Error {
use crate::server::server_api::presigned_upload::HttpStatusError;
anyhow::Error::new(HttpStatusError {
status: 429,
body: "Too Many Requests".to_string(),
@@ -660,9 +654,6 @@ fn permanent_http_error() -> anyhow::Error {
#[tokio::test]
async fn poll_retries_transient_429_errors() {
use crate::server::retry_strategies::MAX_ATTEMPTS;
let mut mock = MockAIClient::new();
let call_count = Arc::new(AtomicUsize::new(0));
@@ -739,7 +730,6 @@ async fn poll_retries_transient_429_errors() {
#[tokio::test]
async fn poll_fails_on_permanent_http_error() {
let mut mock = MockAIClient::new();
mock.expect_spawn_agent().returning(|_| {
@@ -801,8 +791,6 @@ async fn poll_fails_on_permanent_http_error() {
#[tokio::test]
async fn poll_gives_up_after_max_transient_retries() {
let mut mock = MockAIClient::new();
let call_count = Arc::new(AtomicUsize::new(0));
@@ -871,7 +859,6 @@ async fn poll_gives_up_after_max_transient_retries() {
#[tokio::test]
async fn poll_stops_on_terminal_failure_like_state() {
let mut mock = MockAIClient::new();
mock.expect_spawn_agent().returning(|_| {
@@ -1001,7 +988,6 @@ fn session_join_info_ignores_empty_link_and_invalid_session_id() {
#[tokio::test]
async fn poll_for_session_join_info_waits_until_link_is_available() {
let mut mock = MockAIClient::new();
let call_count = Arc::new(AtomicUsize::new(0));
+4 -4
View File
@@ -5,14 +5,14 @@ use chrono::{DateTime, Duration as ChronoDuration, Utc};
#[cfg(not(target_family = "wasm"))]
pub use cloud_object_models::HarnessModelConfig;
pub use cloud_object_models::{AgentConfigSnapshot, HarnessAuthSecretsConfig, HarnessConfig};
use galaxy_core::report_error;
use galaxy_core::ui::theme::{GalaxyTheme, WarpTheme};
use galaxyui::color::ColorU;
use galaxyui::{SingletonEntity, View, ViewContext};
use iso8601_duration::Duration as Iso8601Duration;
use serde::{Deserialize, Serialize};
use session_sharing_protocol::common::SessionId;
use url::Url;
use galaxy_core::report_error;
use galaxy_core::ui::theme::WarpTheme;
use galaxyui::color::ColorU;
use galaxyui::{SingletonEntity, View, ViewContext};
use super::AmbientAgentTaskId;
use crate::ai::artifacts::{deserialize_artifacts, Artifact};
+2
View File
@@ -1,3 +1,5 @@
use galaxy_core::features::FeatureFlag;
use galaxy_core::telemetry::{EnablementState, TelemetryEvent, TelemetryEventDesc};
use serde::Serialize;
use serde_json::{json, Value};
use strum_macros::{EnumDiscriminants, EnumIter};
+3 -3
View File
@@ -7,11 +7,11 @@ use aws_credential_types::provider::error::CredentialsError;
use aws_credential_types::provider::ProvideCredentials;
use futures::channel::oneshot::channel;
use futures::future::BoxFuture;
use tokio::sync::Mutex;
use vec1::vec1;
use galaxy_managed_secrets::client::IdentityTokenOptions;
use galaxy_managed_secrets::ManagedSecretManager;
use galaxyui::{ModelContext, ModelHandle, SingletonEntity};
use tokio::sync::Mutex;
use vec1::vec1;
use crate::settings::{AISettings, AISettingsChangedEvent};
use crate::terminal::event::{AfterBlockCompletedEvent, BlockType, UserBlockCompleted};
@@ -137,7 +137,7 @@ pub async fn load_aws_credentials_from_sdk(
let region_provider = aws_config::meta::region::RegionProviderChain::default_provider();
let loader =
aws_config::defaults(aws_config::BehaviorVersion::latest()).region(region_provider);
let loader = if profile.trim().is_empty() {
let loader = if profile.trim().is_empty() || profile.trim() == "default" {
loader // Let AWS SDK use its default behavior
} else {
loader.profile_name(profile)
+49 -11
View File
@@ -2,17 +2,17 @@ use std::sync::{Arc, Mutex};
use anyhow::Result;
use aws_config::BehaviorVersion;
use aws_credential_types::provider::ProvideCredentials;
use aws_sdk_bedrockruntime::config::Region;
use aws_sdk_bedrockruntime::Client as BedrockRuntimeClient;
use crate::settings::ai::BedrockAuthMethod;
use super::convert::{build_converse_request, CachingConfig, ConversationMessage, ToolDefinition};
use super::diagnostic::BedrockDiagnosticLogger;
use super::external_config::ExternalBedrockConfig;
use super::models::apply_cross_region_prefix;
use super::response_translator::bedrock_stream_to_response_events;
use crate::ai::agent::api::ResponseStream;
use crate::settings::ai::BedrockAuthMethod;
fn strip_context_marker(model_id: &str) -> String {
if let Some(base) = model_id.strip_suffix("[1m]") {
@@ -36,6 +36,7 @@ pub struct BedrockClientConfig {
pub region: String,
pub access_key_id: String,
pub secret_access_key: String,
pub session_token: Option<String>,
pub cross_region_inference: bool,
}
@@ -86,10 +87,23 @@ pub enum BedrockError {
impl BedrockClient {
pub async fn from_config(config: BedrockClientConfig) -> Result<Self, BedrockError> {
log::info!(
"[bedrock] from_config input: auth_method={:?}, profile={:?}, region={:?}, access_key_id_set={}, secret_access_key_set={}, session_token_set={}",
config.auth_method,
config.profile,
config.region,
!config.access_key_id.is_empty(),
!config.secret_access_key.is_empty(),
config.session_token.is_some(),
);
let aws_config = match config.auth_method {
BedrockAuthMethod::Profile | BedrockAuthMethod::Sso => {
let mut loader =
aws_config::defaults(BehaviorVersion::latest()).profile_name(&config.profile);
let mut loader = aws_config::defaults(BehaviorVersion::latest());
if !config.profile.is_empty() && config.profile != "default" {
loader = loader.profile_name(&config.profile);
}
if !config.region.is_empty() {
loader = loader.region(Region::new(config.region.clone()));
@@ -105,7 +119,7 @@ impl BedrockClient {
let creds = aws_credential_types::Credentials::new(
&config.access_key_id,
&config.secret_access_key,
None,
config.session_token,
None,
"warp-bedrock-static",
);
@@ -123,6 +137,24 @@ impl BedrockClient {
}
};
if let Some(provider) = aws_config.credentials_provider() {
match provider.provide_credentials().await {
Ok(creds) => {
log::info!(
"[bedrock] Resolved AWS credentials successfully: access_key_id={:?}, has_session_token={}, expiry={:?}",
creds.access_key_id(),
creds.session_token().is_some(),
creds.expiry(),
);
}
Err(e) => {
log::warn!("[bedrock] Failed to resolve AWS credentials from provider: {e:?}");
}
}
} else {
log::warn!("[bedrock] No credentials provider found in resolved AWS config");
}
let region = aws_config
.region()
.map(|r| r.to_string())
@@ -136,6 +168,7 @@ impl BedrockClient {
})
}
#[allow(clippy::too_many_arguments)]
pub async fn converse_stream(
&self,
model_id: &str,
@@ -174,6 +207,13 @@ impl BedrockClient {
tools.len()
);
log::info!(
"[bedrock] Sending request payload to Bedrock:\nSystem Prompt: {:?}\nMessages: {:#?}\nTools: {:#?}",
system_prompt,
messages,
tools
);
let converted = build_converse_request(
messages.clone(),
system_prompt.clone(),
@@ -302,12 +342,10 @@ impl BedrockClient {
})?;
let mut response_text = String::new();
if let Some(output_msg) = output.output() {
if let aws_sdk_bedrockruntime::types::ConverseOutput::Message(msg) = output_msg {
for block in msg.content() {
if let aws_sdk_bedrockruntime::types::ContentBlock::Text(text) = block {
response_text.push_str(text);
}
if let Some(aws_sdk_bedrockruntime::types::ConverseOutput::Message(msg)) = output.output() {
for block in msg.content() {
if let aws_sdk_bedrockruntime::types::ContentBlock::Text(text) = block {
response_text.push_str(text);
}
}
}
+1 -1
View File
@@ -10,7 +10,6 @@ use aws_smithy_types::Document;
use serde_json::Value as JsonValue;
use super::external_config::ExternalBedrockConfig;
// Re-export shared provider types so existing imports from bedrock::convert continue to work.
pub use crate::ai::provider::types::{
ContentPart, ConversationMessage, MessageContent, MessageRole, ToolDefinition,
@@ -47,6 +46,7 @@ pub struct ConvertedRequest {
pub tool_config: Option<ToolConfiguration>,
}
#[allow(clippy::too_many_arguments)]
pub fn build_converse_request(
messages: Vec<ConversationMessage>,
system_prompt: Option<String>,
+1 -1
View File
@@ -1,4 +1,4 @@
use aws_sdk_bedrockruntime::types::{ContentBlock, ConversationRole};
use aws_sdk_bedrockruntime::types::{ContentBlock, ConversationRole, SystemContentBlock, Tool};
use serde_json::json;
use super::convert::*;
+5 -2
View File
@@ -1,10 +1,13 @@
use chrono::{Local, Utc};
use serde_json::Value as JsonValue;
#![allow(dead_code)]
use std::fs::{self, File, OpenOptions};
use std::io::{BufWriter, Read, Seek, SeekFrom, Write};
use std::path::{Path, PathBuf};
use std::sync::Mutex;
use chrono::{Local, Utc};
use serde_json::Value as JsonValue;
use super::convert::{ConversationMessage, MessageContent, MessageRole, ToolDefinition};
const ENV_VAR: &str = "GALAXY_BEDROCK_DIAGNOSTICS";
+40 -37
View File
@@ -1,13 +1,13 @@
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use futures::StreamExt;
use serde_json::json;
use std::path::PathBuf;
use warp_multi_agent_api as api;
use super::client::{BedrockClient, BedrockClientConfig};
use super::convert::{ConversationMessage, MessageContent, MessageRole, ToolDefinition};
use crate::settings::ai::BedrockAuthMethod;
use warp_multi_agent_api as api;
fn make_user_query_message(id: &str, task_id: &str, query: &str) -> api::Message {
api::Message {
@@ -24,6 +24,7 @@ fn make_user_query_message(id: &str, task_id: &str, query: &str) -> api::Message
mode: None,
intended_agent: 0,
})),
..Default::default()
}
}
@@ -54,6 +55,7 @@ fn make_tool_call_run_shell(
},
)),
})),
..Default::default()
}
}
@@ -81,6 +83,7 @@ fn make_tool_call_read_files(
},
)),
})),
..Default::default()
}
}
@@ -113,12 +116,14 @@ fn make_tool_result_shell(
output: output.into(),
exit_code,
command_id: String::new(),
..Default::default()
},
)),
},
)),
},
)),
..Default::default()
}
}
@@ -155,6 +160,7 @@ fn make_tool_result_read_files(
)),
},
)),
..Default::default()
}
}
@@ -217,6 +223,7 @@ fn make_settings(model: &str) -> api::request::Settings {
supports_bundled_skills: false,
supports_research_agent: false,
supports_orchestration_v2: false,
..Default::default()
}
}
@@ -260,6 +267,7 @@ fn get_test_config() -> Option<BedrockClientConfig> {
region,
access_key_id: String::new(),
secret_access_key: String::new(),
session_token: None,
cross_region_inference: false,
})
}
@@ -691,7 +699,7 @@ impl AgentSimulation {
match name {
"run_shell_command" => {
let command = input["command"].as_str().unwrap_or("echo 'no command'");
let output = std::process::Command::new("sh")
let output = command::blocking::Command::new("sh")
.arg("-c")
.arg(command)
.current_dir(&self.project_path)
@@ -759,7 +767,7 @@ impl AgentSimulation {
.unwrap_or(self.project_path.to_str().unwrap_or("."));
let mut result = String::new();
for query in queries {
let output = std::process::Command::new("grep")
let output = command::blocking::Command::new("grep")
.args(["-rn", query, path])
.output();
if let Ok(out) = output {
@@ -999,7 +1007,7 @@ async fn test_agent_multi_turn_tool_use_produces_output() {
let tool_use_id = format!("tool_{}", total_turns);
let (result, is_error) = match tool.name.as_str() {
"run_shell_command" => {
let ls_output = std::process::Command::new("ls")
let ls_output = command::blocking::Command::new("ls")
.arg("-la")
.current_dir(&project_path)
.output()
@@ -1075,7 +1083,7 @@ async fn test_agent_multi_turn_tool_use_produces_output() {
// 2. No partial reasoning fragments leak to the UI
// 3. The stream protocol is correct (Init, CreateTask, content, Finished)
assert!(
sim.all_text_output.len() > 0 || total_turns > 1,
!sim.all_text_output.is_empty() || total_turns > 1,
"Agent should either produce text or make multiple tool calls to explore"
);
@@ -1160,39 +1168,34 @@ async fn test_reasoning_model_produces_substantial_output() {
while let Some(event_result) = stream.next().await {
event_count += 1;
let event = event_result.expect("event should be Ok");
if let Some(event_type) = &event.r#type {
match event_type {
api::response_event::Type::ClientActions(actions) => {
for action in &actions.actions {
if let Some(action_type) = &action.action {
match action_type {
api::client_action::Action::CreateTask(_) => {
had_create_task = true;
if let Some(api::response_event::Type::ClientActions(actions)) = &event.r#type {
for action in &actions.actions {
if let Some(action_type) = &action.action {
match action_type {
api::client_action::Action::CreateTask(_) => {
had_create_task = true;
}
api::client_action::Action::AddMessagesToTask(add) => {
for msg in &add.messages {
if let Some(api::message::Message::AgentOutput(output)) =
&msg.message
{
total_text.push_str(&output.text);
}
api::client_action::Action::AddMessagesToTask(add) => {
for msg in &add.messages {
if let Some(api::message::Message::AgentOutput(output)) =
&msg.message
{
total_text.push_str(&output.text);
}
}
}
api::client_action::Action::AppendToMessageContent(append) => {
if let Some(msg) = &append.message {
if let Some(api::message::Message::AgentOutput(output)) =
&msg.message
{
total_text.push_str(&output.text);
}
}
}
_ => {}
}
}
api::client_action::Action::AppendToMessageContent(append) => {
if let Some(msg) = &append.message {
if let Some(api::message::Message::AgentOutput(output)) =
&msg.message
{
total_text.push_str(&output.text);
}
}
}
_ => {}
}
}
_ => {}
}
}
}
@@ -1610,7 +1613,7 @@ async fn test_slash_create_new_project() {
if !events.is_empty() {
assert!(
text.len() > 0
!text.is_empty()
|| events
.iter()
.any(|e| matches!(e.event_type, StreamEventType::ToolCallMessage { .. })),
@@ -1645,7 +1648,7 @@ async fn test_slash_auto_code_diff() {
assert_valid_stream(&events, "slash_auto_code_diff");
if !events.is_empty() {
let has_action = text.len() > 0
let has_action = !text.is_empty()
|| events
.iter()
.any(|e| matches!(e.event_type, StreamEventType::ToolCallMessage { .. }));
@@ -1775,7 +1778,7 @@ async fn test_slash_query_with_canned_response() {
assert_valid_stream(&events, "slash_query_with_canned_response");
if !events.is_empty() {
let has_output = text.len() > 0
let has_output = !text.is_empty()
|| events
.iter()
.any(|e| matches!(e.event_type, StreamEventType::ToolCallMessage { .. }));
-1
View File
@@ -144,7 +144,6 @@ fn parse_claude_code_model_map(
model_id: arn,
display_name,
vision_supported: true,
context_size: 200_000,
}
})
.collect()
+109 -17
View File
@@ -22,6 +22,7 @@ fn get_test_config() -> Option<BedrockClientConfig> {
region,
access_key_id: String::new(),
secret_access_key: String::new(),
session_token: None,
cross_region_inference: false,
})
}
@@ -81,6 +82,7 @@ async fn collect_stream_output(
while let Some(event) = stream.next().await {
let event = event.expect("stream event should be Ok");
if let Some(event_type) = event.r#type {
use warp_multi_agent_api::message::Message;
use warp_multi_agent_api::response_event::Type;
match event_type {
Type::ClientActions(actions) => {
@@ -90,24 +92,16 @@ async fn collect_stream_output(
match action_type {
Action::AddMessagesToTask(add) => {
for msg in add.messages {
if let Some(msg_content) = msg.message {
use warp_multi_agent_api::message::Message;
match msg_content {
Message::AgentOutput(output) => {
text.push_str(&output.text);
}
_ => {}
}
if let Some(Message::AgentOutput(output)) = msg.message {
text.push_str(&output.text);
}
}
}
Action::AppendToMessageContent(append) => {
if let Some(msg) = append.message {
if let Some(msg_content) = msg.message {
if let Message::AgentOutput(output) = msg_content {
text.push_str(&output.text);
}
}
if let Some(Message::AgentOutput(output)) =
append.message.and_then(|m| m.message)
{
text.push_str(&output.text);
}
}
_ => {}
@@ -553,9 +547,10 @@ async fn test_arn_based_model() {
return;
};
let arn = std::env::var("BEDROCK_TEST_ARN").unwrap_or_else(|_| {
"arn:aws:bedrock:us-east-1:156729649053:application-inference-profile/1tim45pgo320".into()
});
let Some(arn) = std::env::var("BEDROCK_TEST_ARN").ok() else {
eprintln!("Skipping: BEDROCK_TEST_ARN not set");
return;
};
let client = BedrockClient::from_config(config)
.await
@@ -696,3 +691,100 @@ async fn test_all_tools_visible_to_model() {
tools.len()
);
}
#[tokio::test]
async fn test_bedrock_integration_live_stream() {
let Some(config) = get_test_config() else {
eprintln!("Skipping: BEDROCK_INTEGRATION_TEST not set");
return;
};
println!("[live-test] Initializing Bedrock client...");
let client = BedrockClient::from_config(config)
.await
.expect("client creation");
let model = get_test_model();
println!("[live-test] Using model: {model}");
let messages = vec![ConversationMessage {
role: MessageRole::User,
content: MessageContent::Text(
"Write a 2-line poem about antigravity. Output only the poem.".into(),
),
}];
println!("[live-test] Requesting stream response from Bedrock...");
let stream = client
.converse_stream(
&model,
"live-test-task-id",
true,
messages,
None,
None,
vec![],
1024,
None,
false,
None,
None,
Arc::new(Mutex::new(Vec::new())),
Vec::new(),
)
.await
.expect("converse_stream should succeed");
let mut full_text = String::new();
let mut stream = stream;
while let Some(event) = stream.next().await {
let event = event.expect("stream event should be Ok");
if let Some(event_type) = event.r#type {
use warp_multi_agent_api::message::Message;
use warp_multi_agent_api::response_event::Type;
match event_type {
Type::ClientActions(actions) => {
for action in actions.actions {
if let Some(action_type) = action.action {
use warp_multi_agent_api::client_action::Action;
match action_type {
Action::AddMessagesToTask(add) => {
for msg in add.messages {
if let Some(Message::AgentOutput(output)) = msg.message {
print!("{}", output.text);
full_text.push_str(&output.text);
}
}
}
Action::AppendToMessageContent(append) => {
if let Some(Message::AgentOutput(output)) =
append.message.and_then(|m| m.message)
{
print!("{}", output.text);
full_text.push_str(&output.text);
}
}
_ => {}
}
}
}
}
Type::Finished(finished) => {
println!(
"\n[live-test] Stream finished. Reason: {:?}",
finished.reason
);
if let Some(meta) = finished.conversation_usage_metadata {
if let Some(usage) = meta.byok_token_usage.get("bedrock") {
println!("[live-test] Token Usage - Total: {}", usage.total_tokens);
}
}
}
_ => {}
}
}
}
println!("[live-test] Received full response:\n{}", full_text.trim());
assert!(!full_text.is_empty(), "Model returned an empty response");
}
+2 -3
View File
@@ -1,6 +1,7 @@
use crate::settings::ai::BedrockModelConfig;
#![allow(dead_code)]
use super::external_config::ExternalBedrockConfig;
use crate::settings::ai::BedrockModelConfig;
pub struct DefaultModel {
pub model_id: &'static str,
@@ -128,7 +129,6 @@ pub fn get_effective_models(user_models: &[BedrockModelConfig]) -> Vec<BedrockMo
model_id: m.model_id.to_string(),
display_name: m.display_name.to_string(),
vision_supported: m.vision_supported,
context_size: m.context_size,
})
.collect();
for default in defaults {
@@ -145,7 +145,6 @@ pub fn get_effective_models(user_models: &[BedrockModelConfig]) -> Vec<BedrockMo
model_id: m.model_id.to_string(),
display_name: m.display_name.to_string(),
vision_supported: m.vision_supported,
context_size: m.context_size,
})
.collect()
}
-1
View File
@@ -92,7 +92,6 @@ fn test_get_effective_models_custom_overrides() {
model_id: "custom.model-v1:0".to_string(),
display_name: "Custom Model".to_string(),
vision_supported: false,
context_size: 200_000,
}];
let models = get_effective_models(&custom);
assert_eq!(models.len(), 1);
+12 -3
View File
@@ -1,3 +1,7 @@
#![allow(dead_code)]
use std::collections::HashSet;
use warp_multi_agent_api as api;
use super::convert::{
@@ -357,6 +361,7 @@ fn extract_input_messages(request: &api::Request) -> Vec<api::Message> {
timestamp: None,
server_message_data: String::new(),
citations: vec![],
fetched_memories: vec![],
message: Some(api::message::Message::ToolCallResult(
api::message::ToolCallResult {
tool_call_id: result.tool_call_id.clone(),
@@ -382,6 +387,7 @@ fn extract_input_messages(request: &api::Request) -> Vec<api::Message> {
timestamp: None,
server_message_data: String::new(),
citations: vec![],
fetched_memories: vec![],
message: Some(api::message::Message::UserQuery(
api::message::UserQuery {
query: query.query.clone(),
@@ -422,6 +428,7 @@ fn extract_input_messages(request: &api::Request) -> Vec<api::Message> {
timestamp: None,
server_message_data: String::new(),
citations: vec![],
fetched_memories: vec![],
message: Some(api::message::Message::UserQuery(
api::message::UserQuery {
query: query_text,
@@ -445,6 +452,7 @@ fn extract_input_messages(request: &api::Request) -> Vec<api::Message> {
timestamp: None,
server_message_data: String::new(),
citations: vec![],
fetched_memories: vec![],
message: Some(api::message::Message::UserQuery(api::message::UserQuery {
query: query.query.clone(),
..Default::default()
@@ -462,6 +470,7 @@ fn extract_input_messages(request: &api::Request) -> Vec<api::Message> {
timestamp: None,
server_message_data: String::new(),
citations: vec![],
fetched_memories: vec![],
message: Some(api::message::Message::ToolCallResult(
api::message::ToolCallResult {
tool_call_id: result.tool_call_id.clone(),
@@ -492,6 +501,7 @@ fn extract_input_messages(request: &api::Request) -> Vec<api::Message> {
timestamp: None,
server_message_data: String::new(),
citations: vec![],
fetched_memories: vec![],
message: Some(api::message::Message::UserQuery(api::message::UserQuery {
query: prompt,
..Default::default()
@@ -728,7 +738,6 @@ fn ensure_starts_with_user_message(messages: &mut Vec<ConversationMessage>) {
/// 4. Removes trailing assistant tool_use messages that have no following user message.
/// 5. Ensures strict user/assistant role alternation.
fn ensure_tool_results_paired(messages: &mut Vec<ConversationMessage>) {
// Collect all tool_result IDs that exist anywhere in the conversation.
let mut all_result_ids = HashSet::new();
for msg in messages.iter() {
@@ -1035,7 +1044,7 @@ pub fn extract_tools(request: &api::Request) -> Vec<ToolDefinition> {
let input_schema = tool
.input_schema
.as_ref()
.map(|s| prost_struct_to_json(s))
.map(prost_struct_to_json)
.unwrap_or_else(|| serde_json::json!({"type": "object", "properties": {}}));
tools.push(ToolDefinition {
name,
@@ -1057,7 +1066,7 @@ pub fn extract_tools(request: &api::Request) -> Vec<ToolDefinition> {
let input_schema = tool
.input_schema
.as_ref()
.map(|s| prost_struct_to_json(s))
.map(prost_struct_to_json)
.unwrap_or_else(|| serde_json::json!({"type": "object", "properties": {}}));
tools.push(ToolDefinition {
name,
+45 -41
View File
@@ -1,3 +1,5 @@
#![allow(dead_code)]
use std::sync::{Arc, Mutex};
use aws_sdk_bedrockruntime::operation::converse_stream::ConverseStreamOutput;
@@ -10,11 +12,10 @@ use uuid::Uuid;
use warp_multi_agent_api::response_event::stream_finished;
use warp_multi_agent_api::{self as api, ClientAction, ResponseEvent};
use crate::ai::agent::api::Event;
use crate::server::server_api::AIApiError;
use super::convert::{ContentPart, ConversationMessage, MessageContent, MessageRole};
use super::diagnostic::BedrockDiagnosticLogger;
use crate::ai::agent::api::Event;
use crate::server::server_api::AIApiError;
fn json_to_prost_struct(value: &serde_json::Value) -> prost_types::Struct {
let fields = match value.as_object() {
@@ -57,6 +58,7 @@ pub fn context_window_for_model(model_id: &str) -> u32 {
}
}
#[allow(clippy::too_many_arguments)]
pub fn bedrock_stream_to_response_events(
mut output: ConverseStreamOutput,
task_id: String,
@@ -129,41 +131,36 @@ pub fn bedrock_stream_to_response_events(
}
StreamEvent::ContentBlockStart(block_start) => {
log::debug!("[bedrock] Event #{event_count}: ContentBlockStart");
if let Some(start) = block_start.start() {
match start {
ContentBlockStart::ToolUse(tool_start) => {
_has_tool_calls = true;
if !buffered_text.is_empty() {
let msg_id = current_text_message_id
.clone()
.unwrap_or_else(|| Uuid::new_v4().to_string());
if !text_flushed {
current_text_message_id = Some(msg_id.clone());
text_flushed = true;
log::debug!("[bedrock] Flushing buffered text ({} chars) before tool call", buffered_text.len());
let add_msg = build_add_agent_output_message(
&task_id,
&msg_id,
&buffered_text,
);
yield Ok(add_msg);
} else {
log::debug!("[bedrock] Flushing remaining buffered text ({} chars) as append before tool call", buffered_text.len());
let append = build_append_text(
&task_id,
&msg_id,
&buffered_text,
);
yield Ok(append);
}
buffered_text.clear();
}
current_tool_use_id = tool_start.tool_use_id().to_string();
current_tool_name = tool_start.name().to_string();
current_tool_input_json.clear();
if let Some(ContentBlockStart::ToolUse(tool_start)) = block_start.start() {
_has_tool_calls = true;
if !buffered_text.is_empty() {
let msg_id = current_text_message_id
.clone()
.unwrap_or_else(|| Uuid::new_v4().to_string());
if !text_flushed {
current_text_message_id = Some(msg_id.clone());
text_flushed = true;
log::debug!("[bedrock] Flushing buffered text ({} chars) before tool call", buffered_text.len());
let add_msg = build_add_agent_output_message(
&task_id,
&msg_id,
&buffered_text,
);
yield Ok(add_msg);
} else {
log::debug!("[bedrock] Flushing remaining buffered text ({} chars) as append before tool call", buffered_text.len());
let append = build_append_text(
&task_id,
&msg_id,
&buffered_text,
);
yield Ok(append);
}
_ => {}
buffered_text.clear();
}
current_tool_use_id = tool_start.tool_use_id().to_string();
current_tool_name = tool_start.name().to_string();
current_tool_input_json.clear();
}
}
StreamEvent::ContentBlockDelta(delta) => {
@@ -187,7 +184,7 @@ pub fn bedrock_stream_to_response_events(
// AddMessagesToTask carries enough content for
// the exchange to be fully registered before
// subsequent AppendToMessageContent events arrive.
if buffered_text.len() >= 1 {
if !buffered_text.is_empty() {
let msg_id = Uuid::new_v4().to_string();
current_text_message_id = Some(msg_id.clone());
text_flushed = true;
@@ -201,10 +198,8 @@ pub fn bedrock_stream_to_response_events(
}
}
}
ContentBlockDelta::ReasoningContent(reasoning) => {
if let ReasoningContentBlockDelta::Text(text) = reasoning {
log::trace!("[bedrock] Reasoning delta ({} chars) - not displayed to user", text.len());
}
ContentBlockDelta::ReasoningContent(ReasoningContentBlockDelta::Text(text)) => {
log::trace!("[bedrock] Reasoning delta ({} chars) - not displayed to user", text.len());
}
ContentBlockDelta::ToolUse(tool_delta) => {
current_tool_input_json.push_str(tool_delta.input());
@@ -593,6 +588,7 @@ fn build_user_query_message(task_id: &str, query_text: &str) -> ResponseEvent {
timestamp: None,
server_message_data: String::new(),
citations: vec![],
fetched_memories: vec![],
message: Some(api::message::Message::UserQuery(api::message::UserQuery {
query: query_text.to_string(),
..Default::default()
@@ -682,10 +678,14 @@ pub fn build_stream_finished(
context_window_usage: context_usage,
summarized: is_summarization,
credits_spent: 0.0,
platform_credits_spent: 0.0,
total_input_tokens: input_tokens as u32,
token_usage: vec![],
tool_usage_metadata: None,
warp_token_usage: std::collections::HashMap::new(),
byok_token_usage,
custom_endpoint_token_usage: std::collections::HashMap::new(),
context_window_segments: vec![],
});
ResponseEvent {
@@ -757,6 +757,7 @@ fn build_add_agent_output_message(
timestamp: None,
server_message_data: String::new(),
citations: vec![],
fetched_memories: vec![],
message: Some(api::message::Message::AgentOutput(
api::message::AgentOutput {
text: initial_text.to_string(),
@@ -790,6 +791,7 @@ fn build_append_text(task_id: &str, message_id: &str, text_delta: &str) -> Respo
timestamp: None,
server_message_data: String::new(),
citations: vec![],
fetched_memories: vec![],
message: Some(api::message::Message::AgentOutput(
api::message::AgentOutput {
text: text_delta.to_string(),
@@ -1227,6 +1229,7 @@ pub fn build_tool_call_message(
timestamp: None,
server_message_data: String::new(),
citations: vec![],
fetched_memories: vec![],
message: Some(api::message::Message::ToolCall(api::message::ToolCall {
tool_call_id: effective_tool_call_id,
tool: Some(tool_variant),
@@ -1243,6 +1246,7 @@ pub fn build_tool_call_message(
timestamp: None,
server_message_data: String::new(),
citations: vec![],
fetched_memories: vec![],
message: Some(api::message::Message::AgentOutput(
api::message::AgentOutput {
text: format!(
@@ -1,4 +1,5 @@
use warp_multi_agent_api::{self as api, response_event::stream_finished};
use warp_multi_agent_api::response_event::stream_finished;
use warp_multi_agent_api::{self as api};
use super::response_translator::*;
+5 -4
View File
@@ -1,11 +1,12 @@
use galaxyui::elements::{
Container, CornerRadius, CrossAxisAlignment, Flex, ParentElement, Radius, Text,
};
use galaxyui::{AppContext, Element, Entity, SingletonEntity, TypedActionView, View, ViewContext};
use crate::ai::bedrock::convert::CachingConfig;
use crate::ai::bedrock::external_config::ExternalBedrockConfig;
use crate::appearance::Appearance;
use crate::ui_components::blended_colors;
use galaxyui::{
elements::{Container, CornerRadius, CrossAxisAlignment, Flex, ParentElement, Radius, Text},
AppContext, Element, Entity, SingletonEntity, TypedActionView, View, ViewContext,
};
pub struct SettingsView {
external_config: ExternalBedrockConfig,
@@ -9,16 +9,5 @@ pub fn multiply(a: i32, b: i32) -> i32 {
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
assert_eq!(add(2, 3), 5);
}
#[test]
fn test_multiply() {
assert_eq!(multiply(3, 4), 12);
}
}
#[path = "lib_tests.rs"]
mod tests;
@@ -0,0 +1,11 @@
use super::*;
#[test]
fn test_add() {
assert_eq!(add(2, 3), 5);
}
#[test]
fn test_multiply() {
assert_eq!(multiply(3, 4), 12);
}
+2
View File
@@ -1,3 +1,5 @@
#![allow(dead_code)]
use std::sync::{Arc, Mutex};
use warp_multi_agent_api as api;
+1 -1
View File
@@ -34,10 +34,10 @@ pub use execute::{
StartAgentExecutorEvent, StartAgentRequest, StartAgentRequestId,
};
use futures::future::{join_all, BoxFuture};
use galaxyui::{AppContext, Entity, EntityId, ModelContext, ModelHandle, SingletonEntity};
use itertools::Itertools;
use parking_lot::FairMutex;
use preprocess::{PendingPreprocessedActions, PreprocessId};
use galaxyui::{AppContext, Entity, EntityId, ModelContext, ModelHandle, SingletonEntity};
use self::execute::ask_user_question::AskUserQuestionExecutor;
use self::execute::search_codebase::SearchCodebaseExecutor;
+11 -10
View File
@@ -40,9 +40,20 @@ use futures::future::BoxFuture;
#[cfg(feature = "local_fs")]
use futures::AsyncReadExt;
use futures::FutureExt;
use galaxy_core::execution_mode::AppExecutionMode;
use galaxy_core::features::FeatureFlag;
#[cfg(feature = "local_fs")]
use galaxy_files::{FileModel, TextFileReadResult};
#[cfg(feature = "local_fs")]
use galaxy_util::file::FileLoadError;
#[cfg(feature = "local_fs")]
use galaxy_util::file_type::is_buffer_binary;
use galaxyui::r#async::{Spawnable, SpawnableOutput};
use galaxyui::{AppContext, Entity, EntityId, ModelContext, ModelHandle, SingletonEntity};
use grep::GrepExecutor;
#[cfg(feature = "local_fs")]
use mime_guess::from_path;
use notebooks::NotebookExecutor;
use parking_lot::FairMutex;
use read_documents::ReadDocumentsExecutor;
pub(super) use read_files::ReadFilesExecutor;
@@ -69,16 +80,6 @@ pub use suggest_prompt::PromptSuggestionExecutor;
use upload_artifact::UploadArtifactExecutor;
use use_computer::UseComputerExecutor;
use wait_for_events::WaitForEventsExecutor;
use galaxy_core::execution_mode::AppExecutionMode;
use galaxy_core::features::FeatureFlag;
#[cfg(feature = "local_fs")]
use galaxy_files::{FileModel, TextFileReadResult};
#[cfg(feature = "local_fs")]
use galaxy_util::file::FileLoadError;
#[cfg(feature = "local_fs")]
use galaxy_util::file_type::is_buffer_binary;
use galaxyui::r#async::{Spawnable, SpawnableOutput};
use galaxyui::{AppContext, Entity, EntityId, ModelContext, ModelHandle, SingletonEntity};
use self::search_codebase::SearchCodebaseExecutor;
use crate::ai::agent::conversation::AIConversationId;
@@ -5,7 +5,9 @@ use galaxyui::{Entity, EntityId, ModelContext, SingletonEntity};
use super::{ActionExecution, AnyActionExecution, ExecuteActionInput, PreprocessActionInput};
use crate::ai::agent::{AIAgentActionResultType, AIAgentActionType};
use crate::ai::blocklist::orchestration_events::OrchestrationEventService;
use crate::ai::blocklist::BlocklistAIPermissions;
use crate::BlocklistAIHistoryModel;
pub enum AskUserQuestionDecision {
Completed(Vec<AskUserQuestionAnswerItem>),
@@ -36,6 +36,7 @@ fn build_action(action_id: &str) -> AIAgentAction {
},
task_id: TaskId::new(format!("task-{action_id}")),
requires_result: false,
tool_name: None,
}
}
@@ -1,10 +1,10 @@
use futures::future::BoxFuture;
use futures::FutureExt;
#[cfg(not(target_family = "wasm"))]
use itertools::Itertools;
#[cfg(not(target_family = "wasm"))]
use galaxyui::SingletonEntity;
use galaxyui::{Entity, EntityId, ModelContext, ModelHandle};
#[cfg(not(target_family = "wasm"))]
use itertools::Itertools;
#[cfg(not(target_family = "wasm"))]
use super::get_server_output_id;
@@ -4,10 +4,10 @@ use std::time::Duration;
use futures::future::BoxFuture;
use futures::FutureExt;
use galaxy_core::features::FeatureFlag;
use galaxyui::r#async::FutureExt as AsyncFutureExt;
use galaxyui::{AppContext, Entity, EntityId, ModelContext, ModelHandle, SingletonEntity};
use itertools::Itertools;
use galaxy_core::features::FeatureFlag;
use crate::ai::agent::conversation::AIConversationId;
use crate::ai::agent::{
@@ -3,25 +3,22 @@
//! These tools interact with Galaxy Drive CloudNotebook objects directly,
//! as opposed to the plan tools which work through AIDocumentModel.
use futures::{future::BoxFuture, FutureExt};
use futures::future::BoxFuture;
use futures::FutureExt;
use galaxyui::{Entity, ModelContext, SingletonEntity};
use crate::{
ai::{
agent::{
AIAgentAction, AIAgentActionType, CreateDocumentsRequest, CreateDocumentsResult,
DocumentContext, EditDocumentsRequest, EditDocumentsResult, ReadDocumentsRequest,
ReadDocumentsResult,
},
document::ai_document_model::AIDocumentVersion,
},
cloud_object::model::persistence::CloudModel,
notebooks::CloudNotebookModel,
server::{cloud_objects::update_manager::UpdateManager, ids::ClientId},
workspaces::user_workspaces::UserWorkspaces,
};
use super::{ActionExecution, AnyActionExecution, ExecuteActionInput, PreprocessActionInput};
use crate::ai::agent::{
AIAgentAction, AIAgentActionType, CreateDocumentsRequest, CreateDocumentsResult,
DocumentContext, EditDocumentsRequest, EditDocumentsResult, ReadDocumentsRequest,
ReadDocumentsResult,
};
use crate::ai::document::ai_document_model::AIDocumentVersion;
use crate::cloud_object::model::persistence::CloudModel;
use crate::notebooks::CloudNotebookModel;
use crate::server::cloud_objects::update_manager::UpdateManager;
use crate::server::ids::ClientId;
use crate::workspaces::user_workspaces::UserWorkspaces;
pub struct NotebookExecutor;
@@ -34,6 +34,7 @@ fn read_action(document_id: AIDocumentId) -> AIAgentAction {
action: AIAgentActionType::ReadDocuments(ReadDocumentsRequest {
document_ids: vec![document_id],
}),
tool_name: None,
}
}
@@ -4,16 +4,16 @@ use std::path::PathBuf;
use ai::skills::{parse_skill, ParsedSkill, SkillProvider, SkillReference, SkillScope};
use async_channel::unbounded;
use repo_metadata::repositories::DetectedRepositories;
use repo_metadata::watcher::DirectoryWatcher;
use repo_metadata::RepoMetadataModel;
use tempfile::TempDir;
use galaxy_core::features::FeatureFlag;
use galaxy_core::HostId;
use galaxy_util::local_or_remote_path::LocalOrRemotePath;
use galaxy_util::remote_path::RemotePath;
use galaxy_util::standardized_path::StandardizedPath;
use galaxyui::{App, ModelHandle};
use repo_metadata::repositories::DetectedRepositories;
use repo_metadata::watcher::DirectoryWatcher;
use repo_metadata::RepoMetadataModel;
use tempfile::TempDir;
use watcher::HomeDirectoryWatcher;
use super::*;
@@ -36,7 +36,7 @@ fn initialize_app(app: &mut App) {
app.add_singleton_model(|_| DetectedRepositories::default());
app.add_singleton_model(RepoMetadataModel::new);
app.add_singleton_model(HomeDirectoryWatcher::new_for_test);
app.add_singleton_model(GalaxyManagedPathsWatcher::new_for_testing);
app.add_singleton_model(WarpManagedPathsWatcher::new_for_testing);
app.add_singleton_model(SkillManager::new);
}
fn add_test_read_skill_executor(app: &mut App) -> ModelHandle<ReadSkillExecutor> {
@@ -112,6 +112,7 @@ fn test_read_skill_executor_success() {
}),
task_id: TaskId::new("test-task-id".to_string()),
requires_result: false,
tool_name: None,
};
let input = ExecuteActionInput {
@@ -174,6 +175,7 @@ fn disconnected_remote_session_does_not_fall_back_to_client_global_bundled_skill
}),
task_id: TaskId::new("test-task-id".to_string()),
requires_result: false,
tool_name: None,
};
let input = ExecuteActionInput {
action: &action,
@@ -259,6 +261,7 @@ fn remote_session_reads_remote_bundled_skill_catalog() {
}),
task_id: TaskId::new("test-task-id".to_string()),
requires_result: false,
tool_name: None,
};
let input = ExecuteActionInput {
action: &action,
@@ -307,6 +310,7 @@ fn test_read_skill_executor_reads_enabled_bundled_skill() {
}),
task_id: TaskId::new("test-task-id".to_string()),
requires_result: false,
tool_name: None,
};
let input = ExecuteActionInput {
@@ -351,6 +355,7 @@ fn test_read_skill_executor_rejects_warp_control_bundled_skills_when_disabled()
}),
task_id: TaskId::new(format!("test-task-id-{skill_id}")),
requires_result: false,
tool_name: None,
};
let input = ExecuteActionInput {
@@ -386,6 +391,7 @@ fn test_read_skill_executor_file_not_found() {
}),
task_id: TaskId::new("test-task-id".to_string()),
requires_result: false,
tool_name: None,
};
let input = ExecuteActionInput {
@@ -12,6 +12,7 @@ pub(crate) use diff_application::{apply_edits, FileReadResult};
use futures::channel::oneshot;
use futures::future::BoxFuture;
use futures::FutureExt;
use galaxy_core::send_telemetry_from_ctx;
use itertools::Itertools;
pub(crate) use telemetry::MalformedFinalLineProxyEvent;
#[allow(unused_imports)]
@@ -21,7 +22,6 @@ pub use telemetry::{
RequestFileEditsTelemetryEvent,
};
use vec1::{vec1, Vec1};
use galaxy_core::send_telemetry_from_ctx;
use warp_util::file::FileSaveError;
use warpui::{Entity, EntityId, ModelContext, ModelHandle, SingletonEntity as _, ViewHandle};
@@ -14,9 +14,9 @@ use ai::agent::orchestration_config::OrchestrationConfig;
use ai::skills::SkillReference;
use futures::future::BoxFuture;
use futures::FutureExt;
use galaxy_core::execution_mode::AppExecutionMode;
use settings::Setting;
use warp_cli::agent::Harness;
use galaxy_core::execution_mode::AppExecutionMode;
use warpui::{Entity, EntityId, ModelContext, ModelHandle, SingletonEntity};
use super::start_agent::{StartAgentExecutor, StartAgentOutcome};
@@ -4,9 +4,9 @@ use ai::agent::action::{RunAgentsAgentRunConfig, RunAgentsExecutionMode, RunAgen
use ai::agent::orchestration_config::{
OrchestrationConfig, OrchestrationConfigStatus, OrchestrationExecutionMode,
};
use settings::Setting;
use galaxy_core::execution_mode::ExecutionMode;
use galaxy_core::features::FeatureFlag;
use settings::Setting;
use warpui::{App, Entity, EntityId, ModelHandle};
use super::*;
@@ -239,6 +239,7 @@ fn remote_run_agents_action(harness_type: &str) -> AIAgentAction {
plan_id: String::new(),
harness_auth_secret_name: None,
}),
tool_name: None,
}
}
@@ -4,6 +4,7 @@ use std::path::{Path, PathBuf};
use futures::channel::oneshot;
use futures::future::BoxFuture;
use futures::FutureExt;
use galaxyui::{AppContext, Entity, EntityId, ModelContext, ModelHandle, SingletonEntity};
use itertools::Itertools;
use super::{
@@ -249,6 +249,8 @@ impl ShellCommandExecutor {
block_id: model.block_list().active_block().id().clone(),
output: "Error: Cannot execute command because another command is still running in the terminal.".to_string(),
exit_code: ExitCode::from(1),
start_ts: None,
completed_ts: None,
},
));
}
@@ -2,9 +2,9 @@ use std::collections::HashMap;
use futures::future::BoxFuture;
use futures::FutureExt;
use shell_words::split as split_shell_words;
use galaxy_cli::agent::Harness;
use galaxyui::{Entity, ModelContext, ModelHandle, SingletonEntity};
use shell_words::split as split_shell_words;
use super::{ActionExecution, AnyActionExecution, ExecuteActionInput, PreprocessActionInput};
use crate::ai::agent::conversation::{AIConversationId, ConversationStatus};
@@ -485,7 +485,7 @@ impl StartAgentExecutor {
// In local mode (no parent_run_id), block until the child finishes
// so the parent model receives the child's output as the tool result.
let wait_for_completion = parent_run_id.is_none();
let _wait_for_completion = parent_run_id.is_none();
let (sender, receiver) = async_channel::bounded(1);
let request_id = self.next_request_id();
@@ -45,6 +45,7 @@ fn build_start_agent_action_with_prompt(
},
task_id: TaskId::new("start-agent-task".to_string()),
requires_result: false,
tool_name: None,
}
}
@@ -41,6 +41,7 @@ fn build_upload_artifact_action(file_path: &str) -> AIAgentAction {
}),
task_id: TaskId::new("upload-artifact-task".to_string()),
requires_result: false,
tool_name: None,
}
}
@@ -131,6 +131,7 @@ fn execute_invokes_parent_registration_and_honors_child_short_circuit() {
},
task_id: TaskId::new("wait-task".to_string()),
requires_result: false,
tool_name: None,
};
let execution = executor.update(&mut app, |executor, ctx| {
@@ -18,6 +18,7 @@ fn create_test_action(id: AIAgentActionId) -> AIAgentAction {
citations: vec![],
},
requires_result: false,
tool_name: None,
}
}
@@ -3,9 +3,9 @@
//! Uses the shared [`ChipConfigurator`] with `LeftRightZones` layout to let users
//! drag/drop chips between left, right, and unused banks.
use settings::Setting as _;
use galaxyui::keymap::FixedBinding;
use galaxyui::{AppContext, Element, Entity, SingletonEntity, TypedActionView, View, ViewContext};
use settings::Setting as _;
use super::toolbar_item::AgentToolbarItemKind;
use crate::appearance::AppearanceEvent;
@@ -1,8 +1,5 @@
use std::sync::Arc;
use pathfinder_color::ColorU;
use pathfinder_geometry::vector::vec2f;
use settings::Setting;
use galaxy_core::send_telemetry_from_ctx;
use galaxy_core::ui::color::blend::Blend;
use galaxy_core::ui::theme::Fill;
@@ -14,6 +11,9 @@ use galaxyui::{
AppContext, Element, Entity, ModelHandle, SingletonEntity, TypedActionView, View, ViewContext,
ViewHandle,
};
use pathfinder_color::ColorU;
use pathfinder_geometry::vector::vec2f;
use settings::Setting;
use super::{AgentInputButtonTheme, AmbientAgentViewModel};
use crate::ai::ambient_agents::telemetry::CloudAgentTelemetryEvent;
@@ -13,17 +13,6 @@ use std::time::Duration;
use ai::document::{AIDocumentId, AIDocumentVersion};
use chrono::{DateTime, Local};
use parking_lot::FairMutex;
use pathfinder_color::ColorU;
use pathfinder_geometry::vector::{vec2f, Vector2F};
#[cfg(feature = "voice_input")]
use settings::Setting;
use settings::ToggleableSetting;
#[cfg(not(target_family = "wasm"))]
use tokio::fs;
use toolbar_item::AgentToolbarItemKind;
#[cfg(feature = "voice_input")]
use voice_input::{StartListeningError, VoiceSessionResult};
use galaxy_cli::agent::Harness;
use galaxy_core::context_flag::ContextFlag;
use galaxy_core::report_if_error;
@@ -43,6 +32,17 @@ use galaxyui::{
AppContext, Entity, EntityId, ModelHandle, SingletonEntity, TypedActionView, View, ViewContext,
ViewHandle,
};
use parking_lot::FairMutex;
use pathfinder_color::ColorU;
use pathfinder_geometry::vector::{vec2f, Vector2F};
#[cfg(feature = "voice_input")]
use settings::Setting;
use settings::ToggleableSetting;
#[cfg(not(target_family = "wasm"))]
use tokio::fs;
use toolbar_item::AgentToolbarItemKind;
#[cfg(feature = "voice_input")]
use voice_input::{StartListeningError, VoiceSessionResult};
#[cfg(feature = "local_fs")]
pub(crate) use self::environment_selector::sort_environments_by_recency;
@@ -233,6 +233,9 @@ pub struct AgentInputFooter {
// `Workspace::start_local_to_cloud_handoff`.
handoff_to_cloud_button: ViewHandle<ActionButton>,
start_remote_control_button: ViewHandle<ActionButton>,
stop_remote_control_button: ViewHandle<ActionButton>,
// CLI agent voice input state (self-contained, bypasses editor voice flow).
#[cfg(feature = "voice_input")]
cli_voice_input_state: CLIVoiceInputState,
@@ -579,7 +582,7 @@ impl AgentInputFooter {
let start_remote_control_button = ctx.add_typed_action_view(|_ctx| {
ActionButton::new("/remote-control", RemoteControlButtonTheme)
.with_icon(Icon::Phone01)
.with_tooltip(START_REMOTE_CONTROL_TOOLTIP)
.with_tooltip("Start remote control")
.with_size(cli_button_size)
.with_tooltip_alignment(TooltipAlignment::Left)
.on_click(|ctx| {
@@ -845,6 +848,8 @@ impl AgentInputFooter {
display_chip_config,
fast_forward_button,
handoff_to_cloud_button,
start_remote_control_button,
stop_remote_control_button,
#[cfg(feature = "voice_input")]
cli_voice_input_state: CLIVoiceInputState::default(),
#[cfg(feature = "voice_input")]
@@ -2299,6 +2304,8 @@ pub enum AgentInputFooterAction {
ShowContextMenu {
position: Vector2F,
},
StartRemoteControl,
StopRemoteControl,
}
impl TypedActionView for AgentInputFooter {
@@ -2497,6 +2504,12 @@ impl TypedActionView for AgentInputFooter {
position: *position,
});
}
AgentInputFooterAction::StartRemoteControl => {
ctx.emit(AgentInputFooterEvent::StartRemoteControl);
}
AgentInputFooterAction::StopRemoteControl => {
ctx.emit(AgentInputFooterEvent::StopRemoteControl);
}
}
}
}
@@ -2541,6 +2554,8 @@ pub enum AgentInputFooterEvent {
/// source conversation with content) or activates `&` compose mode
/// (preserving any in-flight prompt).
HandoffChipClicked,
StartRemoteControl,
StopRemoteControl,
}
impl Entity for AgentInputFooter {
@@ -71,6 +71,9 @@ pub enum AgentToolbarItemKind {
// Agent view only "Hand off to cloud" chip.
HandoffToCloud,
// Agent view only "Share session" button.
ShareSession,
}
impl AgentToolbarItemKind {
@@ -81,7 +84,8 @@ impl AgentToolbarItemKind {
| Self::NLDToggle
| Self::ContextWindowUsage
| Self::FastForwardToggle
| Self::HandoffToCloud => ToolbarAvailability::AgentViewOnly,
| Self::HandoffToCloud
| Self::ShareSession => ToolbarAvailability::AgentViewOnly,
Self::FileExplorer | Self::RichInput | Self::Settings => {
ToolbarAvailability::CLIAgentOnly
}
@@ -102,6 +106,7 @@ impl AgentToolbarItemKind {
Self::FastForwardToggle => !status.is_viewer() || status.is_executor(),
// Handoff is host-initiated; viewers cannot hand off another user's conversation.
Self::HandoffToCloud => !status.is_viewer(),
Self::ShareSession => !status.is_viewer(),
Self::ContextChip(_)
| Self::ModelSelector
| Self::NLDToggle
@@ -124,6 +129,7 @@ impl AgentToolbarItemKind {
Self::Settings => "Settings",
Self::FastForwardToggle => "Fast Forward",
Self::HandoffToCloud => "Hand off to cloud",
Self::ShareSession => "Share Session",
}
}
@@ -142,6 +148,7 @@ impl AgentToolbarItemKind {
// The bundled `upload-cloud-01.svg` (cloud-with-upward-arrow) is the
// closest fit among the existing icons for V0; design may swap it later.
Self::HandoffToCloud => Some(Icon::UploadCloud),
Self::ShareSession => Some(Icon::Share),
}
}
@@ -7,11 +7,10 @@ use galaxyui::assets::asset_cache::AssetSource;
use galaxyui::elements::{Container, Element, Empty, MouseStateHandle};
use galaxyui::keymap::Keystroke;
use galaxyui::platform::OperatingSystem;
use galaxyui::{AppContext, Entity, ModelHandle, SingletonEntity, View, ViewContext};
use parking_lot::FairMutex;
use galaxyui::{
AppContext, Entity, ModelHandle, SingletonEntity, TypedActionView, View, ViewContext,
};
use parking_lot::FairMutex;
use super::{AgentViewState, EphemeralMessageModel, EphemeralMessageModelEvent};
use crate::ai::agent::conversation::AIConversation;
@@ -1,6 +1,3 @@
use pathfinder_color::ColorU;
use pathfinder_geometry::vector::Vector2F;
use settings::Setting;
use galaxy_core::ui::appearance::Appearance;
use galaxy_core::ui::Icon;
use galaxyui::elements::{
@@ -16,6 +13,9 @@ use galaxyui::{
AppContext, Element, Entity, EntityId, EventContext, ModelHandle, SingletonEntity,
TypedActionView, View, ViewContext,
};
use pathfinder_color::ColorU;
use pathfinder_geometry::vector::Vector2F;
use settings::Setting;
use super::{AgentViewController, AgentViewEntryOrigin};
use crate::ai::active_agent_views_model::ActiveAgentViewsModel;
@@ -1,12 +1,12 @@
use std::sync::Arc;
use std::time::Duration;
use instant::Instant;
use parking_lot::FairMutex;
use galaxy_core::ui::appearance::Appearance;
use galaxyui::keymap::Keystroke;
use galaxyui::r#async::SpawnedFutureHandle;
use galaxyui::{AppContext, Entity, EntityId, ModelContext, ModelHandle, SingletonEntity};
use instant::Instant;
use parking_lot::FairMutex;
use super::{DismissalStrategy, EphemeralMessage, EphemeralMessageModel};
use crate::ai::agent::conversation::AIConversationId;
@@ -1021,7 +1021,6 @@ fn exit_confirmation_message(
) -> Message {
use galaxyui::SingletonEntity;
let appearance = Appearance::handle(app).as_ref(app);
let (keystroke, text) = match trigger {
@@ -1,12 +1,12 @@
use std::sync::Arc;
use ai::agent::action::{AIAgentActionType, ShellCommandDelay};
use parking_lot::FairMutex;
use galaxy_core::ui::appearance::Appearance;
use galaxyui::elements::{CornerRadius, Radius};
use galaxyui::{
AppContext, Element, Entity, EntityId, ModelHandle, SingletonEntity, View, ViewContext,
};
use parking_lot::FairMutex;
use crate::ai::agent::icons;
use crate::ai::blocklist::block::cli_controller::LongRunningCommandControlState;
+4 -4
View File
@@ -22,15 +22,15 @@ pub use agent_view_block::*;
pub use controller::*;
pub(crate) use conversation_selection::AgentViewConversationSelection;
pub use ephemeral_message_model::*;
use galaxyui::fonts::Properties;
pub use inline_agent_view_header::*;
pub use orchestration_pill_bar::{render_orchestration_breadcrumbs, OrchestrationPillBar};
use pathfinder_color::ColorU;
use galaxy_core::ui::appearance::Appearance;
use galaxy_core::ui::color::blend::Blend;
use galaxy_core::ui::theme::Fill;
use galaxyui::fonts::Properties;
use galaxyui::keymap::Keystroke;
use galaxyui::{AppContext, SingletonEntity};
pub use inline_agent_view_header::*;
pub use orchestration_pill_bar::{render_orchestration_breadcrumbs, OrchestrationPillBar};
use pathfinder_color::ColorU;
pub use zero_state_block::*;
use crate::terminal::model::TerminalModel;
@@ -1,5 +1,3 @@
use pathfinder_color::ColorU;
use pathfinder_geometry::vector::Vector2F;
use galaxy_core::ui::appearance::Appearance;
use galaxy_core::ui::theme::Fill;
use galaxyui::elements::{
@@ -11,6 +9,8 @@ use galaxyui::fonts::Weight::Bold;
use galaxyui::platform::Cursor;
use galaxyui::text_layout::ClipConfig;
use galaxyui::{AppContext, Element, EntityId, EventContext, SingletonEntity};
use pathfinder_color::ColorU;
use pathfinder_geometry::vector::Vector2F;
use crate::ai::agent::api::ServerConversationToken;
use crate::ai::agent::conversation::{AIConversation, AIConversationId};

Some files were not shown because too many files have changed in this diff Show More