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
+213 -14
View File
@@ -1,18 +1,23 @@
use std::collections::HashMap;
use chrono::{Local, Utc};
use persistence::model::{AgentConversationData, ConversationUsageMetadata};
use galaxy_cli::agent::Harness;
use galaxy_core::features::FeatureFlag;
use galaxyui::{App, EntityId};
use persistence::model::AgentConversationData;
use warp_multi_agent_api as api;
use crate::ai::agent::conversation::{AIConversation, AIConversationId};
use crate::ai::ambient_agents::task::{AgentConfigSnapshot, HarnessConfig, TaskCreatorInfo};
use crate::ai::ambient_agents::{AmbientAgentTask, AmbientAgentTaskState};
use crate::ai::blocklist::history_model::BlocklistAIHistoryModel;
use galaxyui::{App, EntityId, SingletonEntity};
use super::{ConversationDetailsData, PanelMode};
use crate::ai::agent::api::ServerConversationToken;
use crate::ai::agent::conversation::{
AIAgentHarness, AIConversation, AIConversationId, ServerAIConversationMetadata,
};
use crate::ai::ambient_agents::task::{AgentConfigSnapshot, HarnessConfig, TaskPrincipalInfo};
use crate::ai::ambient_agents::{AmbientAgentTask, AmbientAgentTaskState};
use crate::ai::blocklist::history_model::BlocklistAIHistoryModel;
use crate::auth::UserUid;
use crate::cloud_object::{Revision, ServerMetadata, ServerPermissions};
use crate::server::ids::ServerId;
use crate::workspaces::user_profiles::UserProfileWithUID;
fn create_test_task(task_id: &str) -> AmbientAgentTask {
let now = Utc::now();
@@ -25,15 +30,17 @@ fn create_test_task(task_id: &str) -> AmbientAgentTask {
created_at: now,
started_at: None,
updated_at: now,
run_time: Some("PT1S".parse().unwrap()),
status_message: None,
source: None,
session_id: None,
session_link: None,
creator: Some(TaskCreatorInfo {
creator: Some(TaskPrincipalInfo {
creator_type: "USER".to_string(),
uid: "user-1".to_string(),
display_name: Some("User 1".to_string()),
}),
executor: None,
conversation_id: None,
request_usage: None,
agent_config_snapshot: None,
@@ -44,8 +51,63 @@ fn create_test_task(task_id: &str) -> AmbientAgentTask {
}
}
#[test]
fn test_from_conversation_prefers_server_creator_profile() {
App::test((), |mut app| async move {
let conversation_id = AIConversationId::new();
let mut conversation = create_restored_conversation(
conversation_id,
"root-task",
"/tmp/server-creator-profile",
AgentConversationData {
server_conversation_token: None,
conversation_usage_metadata: None,
reverted_action_ids: None,
forked_from_server_conversation_token: None,
artifacts_json: None,
parent_agent_id: None,
agent_name: None,
orchestration_harness_type: None,
parent_conversation_id: None,
is_remote_child: false,
root_task_is_optimistic: None,
run_id: None,
autoexecute_override: None,
last_event_sequence: None,
pinned: false,
},
);
conversation.set_server_metadata(create_test_server_metadata(
"server-token-creator-profile",
Some("fallback-uid-that-should-not-render".to_string()),
Some(UserProfileWithUID {
firebase_uid: UserUid::new("creator-profile-uid"),
display_name: Some("ZL".to_string()),
email: "zl@example.com".to_string(),
photo_url: "https://example.com/zl.png".to_string(),
}),
));
app.update(|ctx| {
let data = ConversationDetailsData::from_conversation(&conversation, ctx);
let creator = data
.creator
.as_ref()
.expect("server creator profile should be preserved");
assert_eq!(creator.display_name, "ZL");
assert_eq!(
creator.photo_url.as_deref(),
Some("https://example.com/zl.png")
);
assert_eq!(creator.uid.as_deref(), Some("creator-profile-uid"));
});
});
}
fn create_message_with_directory(id: &str, task_id: &str, directory: &str) -> api::Message {
api::Message {
fetched_memories: vec![],
id: id.to_string(),
task_id: task_id.to_string(),
server_message_data: String::new(),
@@ -71,6 +133,7 @@ fn create_message_with_directory(id: &str, task_id: &str, directory: &str) -> ap
fn create_agent_output_message(id: &str, task_id: &str) -> api::Message {
api::Message {
fetched_memories: vec![],
id: id.to_string(),
task_id: task_id.to_string(),
server_message_data: String::new(),
@@ -107,10 +170,47 @@ fn create_restored_conversation(
.expect("restored conversation should build")
}
fn create_test_server_metadata(
server_token: &str,
creator_uid: Option<String>,
creator: Option<UserProfileWithUID>,
) -> ServerAIConversationMetadata {
ServerAIConversationMetadata {
title: "test conversation".to_string(),
working_directory: None,
harness: AIAgentHarness::Oz,
usage: ConversationUsageMetadata {
was_summarized: false,
context_window_usage: 0.0,
credits_spent: 0.0,
platform_credits_spent: 0.0,
credits_spent_for_last_block: None,
token_usage: vec![],
tool_usage_metadata: Default::default(),
context_window_segments: Vec::new(),
},
metadata: ServerMetadata {
uid: ServerId::default(),
revision: Revision::now(),
metadata_last_updated_ts: Utc::now().into(),
trashed_ts: None,
folder_id: None,
is_welcome_object: false,
creator_uid,
last_editor_uid: None,
current_editor_uid: None,
},
creator,
permissions: ServerPermissions::mock_personal(),
ambient_agent_task_id: None,
server_conversation_token: ServerConversationToken::new(server_token.to_string()),
artifacts: vec![],
}
}
#[test]
fn test_from_task_includes_linked_directory_when_run_id_matches() {
App::test((), |mut app| async move {
let _orchestration_v2_guard = FeatureFlag::OrchestrationV2.override_enabled(true);
let history_model = app.add_singleton_model(|_| BlocklistAIHistoryModel::new(vec![], &[]));
let conversation_id = AIConversationId::new();
@@ -129,12 +229,14 @@ fn test_from_task_includes_linked_directory_when_run_id_matches() {
artifacts_json: None,
parent_agent_id: None,
agent_name: None,
orchestration_harness_type: None,
parent_conversation_id: None,
is_remote_child: false,
root_task_is_optimistic: None,
run_id: Some(task_id.to_string()),
autoexecute_override: None,
last_event_sequence: None,
progressive_summary: None,
messages_summarized_up_to: 0,
pinned: false,
},
);
@@ -225,6 +327,101 @@ fn test_from_task_resolves_harness() {
});
}
#[test]
fn test_from_task_populates_executor() {
App::test((), |mut app| async move {
let _history_model = app.add_singleton_model(|_| BlocklistAIHistoryModel::new(vec![], &[]));
let mut task = create_test_task("550e8400-e29b-41d4-a716-000000004030");
task.executor = Some(TaskPrincipalInfo {
creator_type: "service_account".to_string(),
uid: "agent-uid".to_string(),
display_name: Some("Deploy Agent".to_string()),
});
app.update(|ctx| {
let data = ConversationDetailsData::from_task(&task, None, None, ctx);
assert_eq!(
data.executor
.as_ref()
.map(|executor| executor.display_name.as_str()),
Some("Deploy Agent")
);
});
});
}
#[test]
fn test_from_conversation_populates_local_conversation_fields() {
// Locks in that `ConversationDetailsData::from_conversation` works on native
// and surfaces the conversation-derived fields the conversation details panel
// renders for local Warp Agent runs (APP-3595).
App::test((), |mut app| async move {
let history_model = app.add_singleton_model(|_| BlocklistAIHistoryModel::new(vec![], &[]));
let conversation_id = AIConversationId::new();
let directory = "/tmp/local-conversation-directory";
let conversation = create_restored_conversation(
conversation_id,
"root-task",
directory,
AgentConversationData {
server_conversation_token: None,
conversation_usage_metadata: None,
reverted_action_ids: None,
forked_from_server_conversation_token: None,
artifacts_json: None,
parent_agent_id: None,
agent_name: None,
orchestration_harness_type: None,
parent_conversation_id: None,
run_id: None,
autoexecute_override: None,
last_event_sequence: None,
is_remote_child: false,
root_task_is_optimistic: None,
pinned: false,
},
);
history_model.update(&mut app, |model, ctx| {
model.restore_conversations(EntityId::new(), vec![conversation], ctx);
});
app.update(|ctx| {
let conversation = BlocklistAIHistoryModel::as_ref(ctx)
.conversation(&conversation_id)
.expect("conversation should be present");
let data = ConversationDetailsData::from_conversation(conversation, ctx);
// Mode should be Conversation with the working directory and no server-side
// conversation id (since this conversation was restored without a server token).
match &data.mode {
PanelMode::Conversation {
directory: panel_directory,
server_conversation_id,
ai_conversation_id,
status,
} => {
assert_eq!(panel_directory.as_deref(), Some(directory));
assert!(server_conversation_id.is_none());
// `from_conversation` does not have access to the in-memory
// AIConversationId; that field is populated only by the
// management view path (`from_conversation_metadata`).
assert!(ai_conversation_id.is_none());
assert!(status.is_some());
}
PanelMode::Task { .. } => {
panic!("expected Conversation mode for a local conversation")
}
}
assert_eq!(data.title, "test query");
assert_eq!(data.source_prompt.as_deref(), Some("test query"));
assert!(data.credits.is_some());
});
});
}
#[test]
fn test_from_task_includes_linked_directory_when_server_token_matches() {
App::test((), |mut app| async move {
@@ -246,12 +443,14 @@ fn test_from_task_includes_linked_directory_when_server_token_matches() {
artifacts_json: None,
parent_agent_id: None,
agent_name: None,
orchestration_harness_type: None,
parent_conversation_id: None,
is_remote_child: false,
root_task_is_optimistic: None,
run_id: None,
autoexecute_override: None,
last_event_sequence: None,
progressive_summary: None,
messages_summarized_up_to: 0,
pinned: false,
},
);