first pass of merging in warp (doesn't build)

This commit is contained in:
Ryan Ward
2026-07-01 16:08:58 -05:00
parent 2f64909469
commit 4770ac06b5
3662 changed files with 414574 additions and 89772 deletions
+137 -36
View File
@@ -2,34 +2,33 @@
use std::collections::HashMap;
use std::time::Duration;
use ai::agent::action::{LifecycleEventType as StartAgentLifecycleEventType, ReadSkillRequest};
use ai::agent::action_result::StartAgentVersion;
use ai::agent::convert::ToolToAIAgentActionError;
use ai::agent::UnknownCitationTypeError;
use ai::skills::{
skill_reference_from_api_skill_ref, skill_reference_from_read_skill_ref, SkillPathOrigin,
};
use api::ask_user_question::question::QuestionType;
use galaxy_core::channel::ChannelState;
use warp_multi_agent_api as api;
use crate::ai::agent::api::convert_conversation::{
convert_input_context, convert_tool_call_result_to_input,
};
use crate::ai::agent::comment::CodeReview;
use crate::ai::agent::task::TaskId;
use crate::ai::agent::todos::AIAgentTodoList;
use crate::ai::agent::util::parse_markdown_into_text_and_code_sections;
use crate::ai::agent::{
util::parse_markdown_into_text_and_code_sections, AIAgentAction, AIAgentActionType,
AIAgentCitation, AIAgentInput, AIAgentOutputMessage, AIAgentText, AIAgentTodo,
ArtifactCreatedData, MessageId, StartAgentExecutionMode, SuggestedAgentModeWorkflow,
SuggestedRule, Suggestions, TodoOperation,
};
use crate::ai::agent::{
CloneRepositoryURL, SubagentCall, SubagentType, SummarizationType, WebFetchStatus,
WebSearchStatus,
AIAgentAction, AIAgentActionType, AIAgentAttachment, AIAgentCitation, AIAgentInput,
AIAgentOutputMessage, AIAgentText, AIAgentTodo, ArtifactCreatedData, CloneRepositoryURL,
MessageId, RunAgentsAgentRunConfig, RunAgentsExecutionMode, RunAgentsRequest,
StartAgentExecutionMode, SubagentCall, SubagentType, SuggestedAgentModeWorkflow, SuggestedRule,
Suggestions, SummarizationType, TodoOperation, UserQueryMode, WebFetchStatus, WebSearchStatus,
};
use crate::ai::artifact_download::sanitized_basename;
use crate::ai::document::ai_document_model::{AIDocumentId, AIDocumentVersion};
use ai::agent::action::LifecycleEventType as StartAgentLifecycleEventType;
use ai::agent::action_result::StartAgentVersion;
use ai::agent::convert::ToolToAIAgentActionError;
use ai::agent::UnknownCitationTypeError;
use ai::skills::SkillReference;
use api::ask_user_question::question::QuestionType;
use galaxy_core::channel::ChannelState;
use warp_multi_agent_api as api;
use crate::ai::agent::{AIAgentAttachment, UserQueryMode};
impl TryFrom<api::Attachment> for AIAgentAttachment {
type Error = anyhow::Error;
@@ -53,6 +52,18 @@ impl TryFrom<api::Attachment> for AIAgentAttachment {
}
}
fn convert_read_skill(
read_skill: api::message::tool_call::ReadSkill,
skill_path_origin: &SkillPathOrigin,
) -> Result<AIAgentActionType, ToolToAIAgentActionError> {
let Some(reference) = read_skill.skill_reference else {
return Err(ToolToAIAgentActionError::MissingSkillReference);
};
let skill = skill_reference_from_read_skill_ref(reference, skill_path_origin)
.map_err(|_| ToolToAIAgentActionError::MissingSkillReference)?;
Ok(AIAgentActionType::ReadSkill(ReadSkillRequest { skill }))
}
/// Converts proto UserQueryMode to the internal UserQueryMode type
pub(crate) fn convert_user_query_mode(mode: Option<&api::UserQueryMode>) -> UserQueryMode {
let Some(mode) = mode else {
@@ -81,6 +92,22 @@ fn convert_start_agent_v2_harness_type(
.filter(|harness_type| !harness_type.trim().is_empty())
}
/// Maps the proto `Harness` oneof to a client-side string identifier
/// (e.g. "oz", "claude"). Returns `None` for an unset variant.
pub(crate) fn convert_run_agents_harness(harness: Option<&api::Harness>) -> Option<String> {
let variant = harness?.variant.as_ref()?;
Some(
match variant {
api::harness::Variant::Oz(_) => "oz",
api::harness::Variant::ClaudeCode(_) => "claude",
api::harness::Variant::OpenCode(_) => "opencode",
api::harness::Variant::Gemini(_) => "gemini",
api::harness::Variant::Codex(_) => "codex",
}
.to_string(),
)
}
fn convert_start_agent_execution_mode(
execution_mode: Option<api::start_agent::ExecutionMode>,
) -> StartAgentExecutionMode {
@@ -94,8 +121,62 @@ fn convert_start_agent_execution_mode(
}
}
fn convert_run_agents_execution_mode(
execution_mode: Option<api::run_agents::ExecutionMode>,
) -> RunAgentsExecutionMode {
match execution_mode {
Some(api::run_agents::ExecutionMode::Remote(remote)) => RunAgentsExecutionMode::Remote {
environment_id: remote.environment_id,
worker_host: remote.worker_host,
computer_use_enabled: remote.computer_use_enabled,
},
Some(api::run_agents::ExecutionMode::Local(_)) | None => RunAgentsExecutionMode::Local,
}
}
fn convert_run_agents(
run_agents: api::RunAgents,
skill_path_origin: &SkillPathOrigin,
) -> AIAgentActionType {
let api::RunAgents {
summary,
base_prompt,
skills,
model_id,
harness,
agent_run_configs,
execution_mode,
plan_id,
} = run_agents;
AIAgentActionType::RunAgents(RunAgentsRequest {
summary,
base_prompt,
skills: skills
.into_iter()
.filter_map(|skill| skill_reference_from_api_skill_ref(skill, skill_path_origin))
.collect(),
model_id,
harness_type: convert_run_agents_harness(harness.as_ref()).unwrap_or_default(),
execution_mode: convert_run_agents_execution_mode(execution_mode),
agent_run_configs: agent_run_configs
.into_iter()
.map(|config| RunAgentsAgentRunConfig {
name: config.name,
prompt: config.prompt,
title: config.title,
})
.collect(),
plan_id,
// Auth secret is a client-side dispatch concern populated by the
// confirmation card from `CloudAgentSettings.last_selected_auth_secret`
// before Accept. The proto does not carry it.
harness_auth_secret_name: None,
})
}
fn convert_start_agent_v2_execution_mode(
execution_mode: Option<api::start_agent_v2::ExecutionMode>,
skill_path_origin: &SkillPathOrigin,
) -> StartAgentExecutionMode {
match execution_mode.and_then(|execution_mode| execution_mode.mode) {
Some(api::start_agent_v2::execution_mode::Mode::Remote(remote)) => {
@@ -104,7 +185,9 @@ fn convert_start_agent_v2_execution_mode(
skill_references: remote
.skills
.into_iter()
.filter_map(convert_skill_reference)
.filter_map(|skill| {
skill_reference_from_api_skill_ref(skill, skill_path_origin)
})
.collect(),
model_id: remote.model_id,
computer_use_enabled: remote.computer_use_enabled,
@@ -112,6 +195,9 @@ fn convert_start_agent_v2_execution_mode(
harness_type: convert_start_agent_v2_harness_type(remote.harness)
.unwrap_or_default(),
title: remote.title,
// Auth secret is plumbed client-side via `RunAgentsRequest`;
// StartAgentV2 from the server never carries it.
auth_secret_name: None,
}
}
Some(api::start_agent_v2::execution_mode::Mode::Local(local)) => {
@@ -123,16 +209,6 @@ fn convert_start_agent_v2_execution_mode(
}
}
fn convert_skill_reference(skill_ref: api::SkillRef) -> Option<SkillReference> {
match skill_ref.skill_reference {
Some(api::skill_ref::SkillReference::Path(path)) => Some(SkillReference::Path(path.into())),
Some(api::skill_ref::SkillReference::BundledSkillId(id)) => {
Some(SkillReference::BundledSkillId(id))
}
None => None,
}
}
/// Unexpected errors when trying to convert an [`api::Message`] to an [`AIAgentOutputMessage`].
#[derive(Debug, thiserror::Error)]
pub enum MessageToAIAgentOutputMessageError {
@@ -167,6 +243,7 @@ pub struct ConversionParams<'a> {
pub task_id: &'a TaskId,
pub current_todo_list: Option<&'a AIAgentTodoList>,
pub active_code_review: Option<&'a CodeReview>,
pub skill_path_origin: &'a SkillPathOrigin,
}
/// Trait for converting an [`api::Message`] to an [`AIAgentOutputMessage`].
@@ -569,7 +646,11 @@ impl ConvertAPIMessageToClientOutputMessage for api::Message {
| api::message::Message::CodeReview(_)
| api::message::Message::ServerEvent(_)
| api::message::Message::InvokeSkill(_)
| api::message::Message::PassiveSuggestionResult(_) => {
| api::message::Message::PassiveSuggestionResult(_)
// Stage 2 plan-card config snapshot: hydrated separately by the
// plan card's `AIDocumentModel` subscription, not via the
// exchange/output stream. No client output message representation.
| api::message::Message::OrchestrationConfigSnapshot(_) => {
Ok(MaybeAIAgentOutputMessage::NoClientRepresentation)
}
}
@@ -600,7 +681,7 @@ trait ConvertAPIToolCallToAIAgentAction {
) -> Result<MaybeAIAgentAction, ToolToAIAgentActionError>;
}
/// Trys to convert an [`api::message::ToolCall`] to an [`AIAgentAction`].
/// Tries to convert an [`api::message::ToolCall`] to an [`AIAgentAction`].
///
/// A [`Result::Error`] indicates an unexpected problem, while [`Ok(None)`]
/// indicates a tool call that we aren't expected to parse.
@@ -700,6 +781,7 @@ impl ConvertAPIToolCallToAIAgentAction for api::message::ToolCall {
create_standard_action(request_computer_use.into())
}
api::message::tool_call::Tool::Subagent(subagent) => {
use api::message::tool_call::subagent::conversation_search_metadata::Target;
use api::message::tool_call::subagent::Metadata;
let subagent_type = match subagent.metadata {
Some(Metadata::Cli(_)) => SubagentType::Cli,
@@ -713,14 +795,23 @@ impl ConvertAPIToolCallToAIAgentAction for api::message::ToolCall {
} else {
Some(cs_meta.query)
};
let conversation_id = if cs_meta.conversation_id.is_empty() {
None
} else {
Some(cs_meta.conversation_id)
let (conversation_id, agent_run_id) = match cs_meta.target {
Some(Target::ConversationId(conversation_id))
if !conversation_id.is_empty() =>
{
(Some(conversation_id), None)
}
Some(Target::AgentRunId(agent_run_id)) if !agent_run_id.is_empty() => {
(None, Some(agent_run_id))
}
Some(Target::ConversationId(_))
| Some(Target::AgentRunId(_))
| None => (None, None),
};
SubagentType::ConversationSearch {
query,
conversation_id,
agent_run_id,
}
}
Some(Metadata::WarpDocumentationSearch(_)) => {
@@ -757,6 +848,7 @@ impl ConvertAPIToolCallToAIAgentAction for api::message::ToolCall {
prompt: start_agent.prompt,
execution_mode: convert_start_agent_v2_execution_mode(
start_agent.execution_mode,
params.skill_path_origin,
),
lifecycle_subscription: start_agent.lifecycle_subscription.map(
|subscription| {
@@ -769,6 +861,9 @@ impl ConvertAPIToolCallToAIAgentAction for api::message::ToolCall {
),
})
}
api::message::tool_call::Tool::RunAgents(orchestrate) => {
create_standard_action(convert_run_agents(orchestrate, params.skill_path_origin))
}
api::message::tool_call::Tool::SendMessageToAgent(send_message) => {
create_standard_action(AIAgentActionType::SendMessageToAgent {
addresses: send_message.addresses,
@@ -780,7 +875,7 @@ impl ConvertAPIToolCallToAIAgentAction for api::message::ToolCall {
create_standard_action(insert_review_comments.into())
}
api::message::tool_call::Tool::ReadSkill(read_skill) => {
create_standard_action(read_skill.try_into()?)
create_standard_action(convert_read_skill(read_skill, params.skill_path_origin)?)
}
api::message::tool_call::Tool::FetchConversation(fetch_conversation) => {
create_standard_action(fetch_conversation.into())
@@ -798,6 +893,12 @@ impl ConvertAPIToolCallToAIAgentAction for api::message::ToolCall {
api::message::tool_call::Tool::Server(_) => {
Ok(MaybeAIAgentAction::NoClientRepresentation)
}
api::message::tool_call::Tool::WaitForEvents(payload) => {
create_standard_action(AIAgentActionType::WaitForEvents {
tool_call_id: self.tool_call_id.clone(),
idle_timeout_seconds: payload.idle_timeout_seconds,
})
}
_ => Err(ToolToAIAgentActionError::UnexpectedTool),
}
}