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
+73 -60
View File
@@ -1,44 +1,35 @@
pub mod helper;
pub mod transaction;
use std::{
collections::{HashMap, HashSet},
fmt::Display,
ops::Deref,
};
use std::collections::{HashMap, HashSet};
use std::fmt::Display;
use std::ops::Deref;
use chrono::DateTime;
use ai::skills::SkillPathOrigin;
use field_mask::{FieldMaskError, FieldMaskOperation};
use helper::{MessageExt, SubagentExt, ToolCallExt};
use itertools::Itertools;
use prost_types::FieldMask;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use warp_multi_agent_api::{
self as api,
message::{tool_call::subagent::Metadata, Message},
};
use warp_multi_agent_api::message::tool_call::subagent::Metadata;
use warp_multi_agent_api::message::Message;
use warp_multi_agent_api::{self as api};
use crate::{
ai::{
agent::comment::CodeReview,
document::ai_document_model::{AIDocumentId, AIDocumentVersion},
},
server::datetime_ext::DateTimeExt,
terminal::model::block::BlockId,
AIAgentTodoList,
use super::api::convert_conversation::convert_tool_call_result_to_input;
use super::api::{
user_inputs_from_messages, ConversionParams, ConvertAPIMessageToClientOutputMessage,
};
use super::comment::CodeReview;
use super::conversation::{context_in_exchanges, update_todo_list_from_todo_op};
use super::{
api::{
convert_conversation::convert_tool_call_result_to_input, user_inputs_from_messages,
ConversionParams, ConvertAPIMessageToClientOutputMessage,
},
conversation::{context_in_exchanges, update_todo_list_from_todo_op},
AIAgentContext, AIAgentExchange, AIAgentExchangeId, AIAgentOutput, AIAgentOutputMessage,
AIAgentOutputStatus, MaybeAIAgentOutputMessage, MessageId, MessageToAIAgentOutputMessageError,
Shared,
};
use crate::ai::document::ai_document_model::{AIDocumentId, AIDocumentVersion};
use crate::terminal::model::block::BlockId;
use crate::AIAgentTodoList;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct TaskId(String);
@@ -128,7 +119,6 @@ struct ServerTask {
}
mod optimistic {
use crate::terminal::model::block::BlockId;
#[derive(Debug, Clone)]
pub(super) struct CLIAgentSubtask {
@@ -176,6 +166,12 @@ pub struct Task {
/// List of `AIAgentExchange`s corresponding to messages contained in this task.
exchanges: Vec<AIAgentExchange>,
}
#[derive(Clone, Copy)]
pub(super) struct TaskMessageContext<'a> {
pub(super) current_todo_list: Option<&'a AIAgentTodoList>,
pub(super) active_code_review: Option<&'a CodeReview>,
pub(super) skill_path_origin: &'a SkillPathOrigin,
}
impl Task {
pub(super) fn new_optimistic_root() -> Self {
@@ -236,6 +232,7 @@ impl Task {
parent_task: Option<&api::Task>,
current_todo_list: Option<&AIAgentTodoList>,
active_code_review: Option<&CodeReview>,
skill_path_origin: &SkillPathOrigin,
) -> Result<Self, UpgradeOptimisticTaskError> {
match self.data {
TaskImpl::Optimistic(optimistic::Task::Root) => {
@@ -281,8 +278,11 @@ impl Task {
if let Err(e) = self.update_exchange_from_messages(
messages,
exchange_id,
current_todo_list,
active_code_review,
TaskMessageContext {
current_todo_list,
active_code_review,
skill_path_origin,
},
false,
) {
log::error!(
@@ -315,7 +315,8 @@ impl Task {
parent_task: &api::Task,
existing_exchange: &AIAgentExchange,
current_todo_list: Option<&AIAgentTodoList>,
current_comment_state: Option<&CodeReview>,
active_code_review: Option<&CodeReview>,
skill_path_origin: &SkillPathOrigin,
should_convert_input_messages: bool,
) -> Self {
let subagent_call_and_id = parent_task.messages.iter().find_map(|message| {
@@ -330,7 +331,7 @@ impl Task {
input: vec![],
output_status: AIAgentOutputStatus::Streaming { output: None },
added_message_ids: Default::default(),
start_time: DateTime::now().into(),
start_time: chrono::Local::now(),
finish_time: None,
time_to_first_token_ms: None,
working_directory: existing_exchange.working_directory.clone(),
@@ -368,8 +369,11 @@ impl Task {
me.update_exchange_from_messages(
messages_clone,
new_exchange_id,
current_todo_list,
current_comment_state,
TaskMessageContext {
current_todo_list,
active_code_review,
skill_path_origin,
},
should_convert_input_messages,
)
.expect("Exchange exists and output is in 'streaming' state.");
@@ -462,7 +466,7 @@ impl Task {
input: vec![],
output_status: AIAgentOutputStatus::Streaming { output: None },
added_message_ids: Default::default(),
start_time: DateTime::now().into(),
start_time: chrono::Local::now(),
finish_time: None,
time_to_first_token_ms: None,
working_directory: existing_exchange.working_directory.clone(),
@@ -603,6 +607,19 @@ impl Task {
self.try_get_source().ok()
}
pub(super) fn source_for_persistence(&self) -> Option<api::Task> {
match &self.data {
TaskImpl::Server(server_data) => Some(server_data.source.clone()),
// Optimistic root tasks have a client-generated UUID and no
// server-side identity yet. Persisting a stub `api::Task` for them
// produces an orphan row in `agent_tasks` that survives the later
// server-side upgrade and breaks restore by competing with the
// real server root for parentless-task selection. See QUALITY-774.
TaskImpl::Optimistic(optimistic::Task::Root) => None,
TaskImpl::Optimistic(optimistic::Task::CLIAgent(_)) => None,
}
}
pub fn messages(&self) -> impl Iterator<Item = &api::Message> {
self.source()
.into_iter()
@@ -677,8 +694,7 @@ impl Task {
&mut self,
messages: Vec<api::Message>,
exchange_id: AIAgentExchangeId,
current_todo_list: Option<&AIAgentTodoList>,
current_comments: Option<&CodeReview>,
message_context: TaskMessageContext<'_>,
should_convert_input_messages: bool,
) -> Result<(), UpdateTaskError> {
if self.source().is_none() {
@@ -687,8 +703,7 @@ impl Task {
self.update_exchange_from_messages(
messages.clone(),
exchange_id,
current_todo_list,
current_comments,
message_context,
should_convert_input_messages,
)?;
self.try_get_source_mut()?.messages.extend(messages);
@@ -699,8 +714,7 @@ impl Task {
&mut self,
message: api::Message,
exchange_id: AIAgentExchangeId,
current_todo_list: Option<&AIAgentTodoList>,
current_comments: Option<&CodeReview>,
message_context: TaskMessageContext<'_>,
mask: FieldMask,
should_convert_input_messages: bool,
) -> Result<&api::Message, UpdateTaskError> {
@@ -714,8 +728,7 @@ impl Task {
self.add_messages(
vec![message.clone()],
exchange_id,
current_todo_list,
current_comments,
message_context,
should_convert_input_messages,
)?;
return self
@@ -734,10 +747,13 @@ impl Task {
.exchange_mut(exchange_id)
.ok_or(UpdateTaskError::ExchangeNotFound)?;
exchange_to_update.upsert_output_for_message(
&id,
&updated_message,
current_todo_list,
current_comments,
ConversionParams {
task_id: &id,
current_todo_list: message_context.current_todo_list,
active_code_review: message_context.active_code_review,
skill_path_origin: message_context.skill_path_origin,
},
)?;
// Task message updates can carry tool call result updates with them,
@@ -782,8 +798,7 @@ impl Task {
&mut self,
message: api::Message,
exchange_id: AIAgentExchangeId,
current_todo_list: Option<&AIAgentTodoList>,
current_comments: Option<&CodeReview>,
message_context: TaskMessageContext<'_>,
mask: FieldMask,
) -> Result<&api::Message, UpdateTaskError> {
let Some((idx, existing_message)) = self
@@ -836,10 +851,13 @@ impl Task {
.exchange_mut(exchange_id)
.ok_or(UpdateTaskError::ExchangeNotFound)?;
exchange_to_update.upsert_output_for_message(
&id,
&updated_message,
current_todo_list,
current_comments,
ConversionParams {
task_id: &id,
current_todo_list: message_context.current_todo_list,
active_code_review: message_context.active_code_review,
skill_path_origin: message_context.skill_path_origin,
},
)?;
let source = self.try_get_source_mut()?;
@@ -964,8 +982,7 @@ impl Task {
&mut self,
messages: Vec<api::Message>,
exchange_id: AIAgentExchangeId,
current_todo_list: Option<&AIAgentTodoList>,
active_code_review: Option<&CodeReview>,
message_context: TaskMessageContext<'_>,
should_convert_input_messages: bool,
) -> Result<(), UpdateTaskError> {
let exchange = self
@@ -1007,8 +1024,9 @@ impl Task {
.filter_map(|m| {
match m.to_client_output_message(ConversionParams {
task_id: &self.id,
current_todo_list,
active_code_review,
current_todo_list: message_context.current_todo_list,
active_code_review: message_context.active_code_review,
skill_path_origin: message_context.skill_path_origin,
}) {
Ok(MaybeAIAgentOutputMessage::Message(m)) => Some(Ok(m)),
Ok(MaybeAIAgentOutputMessage::NoClientRepresentation) => None,
@@ -1043,10 +1061,8 @@ impl AIAgentExchange {
/// Note: this means updates will insert a new entry after previously added entries.
fn upsert_output_for_message(
&self,
task_id: &TaskId,
task_message: &api::Message,
todo_list: Option<&AIAgentTodoList>,
comments: Option<&CodeReview>,
conversion_params: super::api::ConversionParams<'_>,
) -> Result<(), UpdateTaskError> {
if let AIAgentOutputStatus::Streaming {
output: Some(output),
@@ -1079,11 +1095,8 @@ impl AIAgentExchange {
match task_message
.clone()
.to_client_output_message(ConversionParams {
current_todo_list: todo_list,
active_code_review: comments,
task_id,
})? {
.to_client_output_message(conversion_params)?
{
MaybeAIAgentOutputMessage::Message(m) => {
log::info!(
"[bedrock-debug] upsert_output_for_message: client_message_type={:?}",