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
+191 -9
View File
@@ -1,12 +1,20 @@
use warp_multi_agent_api::{
self as api,
apply_file_diffs_result::success::UpdatedFileContent,
ask_user_question_result::answer_item::{self, Answer as AskUserQuestionAnswer},
use chrono::{DateTime, Local};
use warp_multi_agent_api::apply_file_diffs_result::success::UpdatedFileContent;
use warp_multi_agent_api::ask_user_question_result::answer_item::{
self, Answer as AskUserQuestionAnswer,
};
use crate::agent::{action_result::ShellCommandError, convert::ConvertToAPITypeError};
use warp_multi_agent_api::{self as api};
use super::*;
use crate::agent::action_result::ShellCommandError;
use crate::agent::convert::ConvertToAPITypeError;
fn local_datetime_to_timestamp(timestamp: DateTime<Local>) -> prost_types::Timestamp {
prost_types::Timestamp {
seconds: timestamp.timestamp(),
nanos: timestamp.timestamp_subsec_nanos() as i32,
}
}
impl TryFrom<RequestCommandOutputResult> for api::request::input::tool_call_result::Result {
type Error = ConvertToAPITypeError;
@@ -18,7 +26,8 @@ impl TryFrom<RequestCommandOutputResult> for api::request::input::tool_call_resu
block_id,
output,
exit_code,
..
start_ts,
completed_ts,
} => Ok(
api::request::input::tool_call_result::Result::RunShellCommand(
#[allow(deprecated)]
@@ -31,6 +40,8 @@ impl TryFrom<RequestCommandOutputResult> for api::request::input::tool_call_resu
command_id: block_id.to_string(),
output,
exit_code: exit_code.value(),
start_ts: start_ts.map(local_datetime_to_timestamp),
finish_ts: completed_ts.map(local_datetime_to_timestamp),
},
)),
},
@@ -127,7 +138,7 @@ impl TryFrom<WriteToLongRunningShellCommandResult>
},
),
),
WriteToLongRunningShellCommandResult::CommandFinished { block_id, output, exit_code, .. } => Ok(
WriteToLongRunningShellCommandResult::CommandFinished { block_id, output, exit_code, start_ts, completed_ts } => Ok(
api::request::input::tool_call_result::Result::WriteToLongRunningShellCommand(
api::WriteToLongRunningShellCommandResult {
result: Some(api::write_to_long_running_shell_command_result::Result::CommandFinished(
@@ -135,6 +146,8 @@ impl TryFrom<WriteToLongRunningShellCommandResult>
command_id: block_id.to_string(),
output,
exit_code: exit_code.value(),
start_ts: start_ts.map(local_datetime_to_timestamp),
finish_ts: completed_ts.map(local_datetime_to_timestamp),
}
))
},
@@ -797,7 +810,8 @@ impl TryFrom<ReadShellCommandOutputResult> for api::request::input::tool_call_re
block_id,
output,
exit_code,
..
start_ts,
completed_ts,
} => Ok(
api::request::input::tool_call_result::Result::ReadShellCommandOutput(
api::ReadShellCommandOutputResult {
@@ -807,6 +821,8 @@ impl TryFrom<ReadShellCommandOutputResult> for api::request::input::tool_call_re
command_id: block_id.to_string(),
output,
exit_code: exit_code.value(),
start_ts: start_ts.map(local_datetime_to_timestamp),
finish_ts: completed_ts.map(local_datetime_to_timestamp),
},
)),
},
@@ -891,6 +907,8 @@ impl TryFrom<TransferShellCommandControlToUserResult>
block_id,
output,
exit_code,
start_ts,
completed_ts,
} => Ok(
api::request::input::tool_call_result::Result::TransferShellCommandControlToUser(
api::TransferShellCommandControlToUserResult {
@@ -900,6 +918,8 @@ impl TryFrom<TransferShellCommandControlToUserResult>
command_id: block_id.to_string(),
output,
exit_code: exit_code.value(),
start_ts: start_ts.map(local_datetime_to_timestamp),
finish_ts: completed_ts.map(local_datetime_to_timestamp),
},
),
),
@@ -1109,6 +1129,7 @@ impl TryFrom<RequestComputerUseResult> for api::request::input::tool_call_result
RequestComputerUseResult::Approved {
screenshot,
platform,
windows,
} => Ok(
api::request::input::tool_call_result::Result::RequestComputerUse(
api::RequestComputerUseResult {
@@ -1125,6 +1146,7 @@ impl TryFrom<RequestComputerUseResult> for api::request::input::tool_call_result
height: screenshot.height as i32,
}),
platform: convert_platform(platform).into(),
windows: windows.into_iter().map(convert_window_info).collect(),
},
)),
},
@@ -1158,6 +1180,9 @@ impl TryFrom<UseComputerResult> for api::request::input::tool_call_result::Resul
fn try_from(result: UseComputerResult) -> Result<Self, Self::Error> {
match result {
UseComputerResult::Success(result) => {
// Copy out the captured-window metadata (if any) before the owned fields of
// `result` are moved into the message below.
let captured = result.captured_window;
Ok(api::request::input::tool_call_result::Result::UseComputer(
api::UseComputerResult {
result: Some(api::use_computer_result::Result::Success(
@@ -1169,6 +1194,20 @@ impl TryFrom<UseComputerResult> for api::request::input::tool_call_result::Resul
height: s.height as i32,
}),
cursor_position: result.cursor_position.map(vec_to_coordinates),
windows: result
.windows
.into_iter()
.map(convert_window_info)
.collect(),
// The window id is an opaque string on the wire; on macOS it is a
// CGWindowID, so format the u32 back to a string at the boundary.
captured_window: captured.map(|c| {
api::use_computer_result::success::CapturedWindow {
window_id: c.window_id.to_string(),
width_px: c.width_px,
height_px: c.height_px,
}
}),
},
)),
},
@@ -1205,6 +1244,18 @@ fn vec_to_coordinates(vec: computer_use::Vector2I) -> api::Coordinates {
}
}
/// Converts a computer_use window record into the API `WindowInfo` message.
fn convert_window_info(window: computer_use::WindowInfo) -> api::WindowInfo {
api::WindowInfo {
// The window id travels as an opaque string; on macOS it is a CGWindowID (u32).
window_id: window.window_id.to_string(),
pid: window.pid,
app_name: window.app_name,
title: window.title,
layer: window.layer,
}
}
fn convert_platform(
platform: computer_use::Platform,
) -> api::request_computer_use_result::approved::Platform {
@@ -1463,6 +1514,121 @@ impl From<AskUserQuestionResult> for api::request::input::tool_call_result::Resu
}
}
impl From<RunAgentsLaunchedExecutionMode>
for api::run_agents_result::launched::ResolvedExecutionMode
{
fn from(mode: RunAgentsLaunchedExecutionMode) -> Self {
match mode {
RunAgentsLaunchedExecutionMode::Local => {
api::run_agents_result::launched::ResolvedExecutionMode::Local(
api::run_agents::Local {},
)
}
RunAgentsLaunchedExecutionMode::Remote {
environment_id,
worker_host,
computer_use_enabled,
} => api::run_agents_result::launched::ResolvedExecutionMode::Remote(
api::run_agents::Remote {
environment_id,
worker_host,
computer_use_enabled,
},
),
}
}
}
impl From<RunAgentsAgentOutcome> for api::run_agents_result::AgentOutcome {
fn from(outcome: RunAgentsAgentOutcome) -> Self {
let result = match outcome.kind {
RunAgentsAgentOutcomeKind::Launched { agent_id } => {
api::run_agents_result::agent_outcome::Result::Launched(
api::run_agents_result::LaunchedAgent { agent_id },
)
}
RunAgentsAgentOutcomeKind::Failed { error } => {
api::run_agents_result::agent_outcome::Result::Failed(
api::run_agents_result::FailedAgent { error },
)
}
};
api::run_agents_result::AgentOutcome {
name: outcome.name,
result: Some(result),
}
}
}
/// Maps a client-side harness string identifier (e.g. "oz", "claude")
/// to the new proto `Harness` oneof. Returns `None` for empty,
/// unrecognized, or `"unknown"` strings; callers leave
/// `resolved_harness` unset in that case.
pub(super) fn build_api_harness(harness_type: &str) -> Option<api::Harness> {
let normalized = harness_type.trim().to_ascii_lowercase().replace('_', "-");
let variant = match normalized.as_str() {
"oz" => api::harness::Variant::Oz(api::harness::Oz {}),
"claude" | "claude-code" => api::harness::Variant::ClaudeCode(api::harness::ClaudeCode {}),
"opencode" | "open-code" => api::harness::Variant::OpenCode(api::harness::OpenCode {}),
"gemini" => api::harness::Variant::Gemini(api::harness::Gemini {}),
"codex" => api::harness::Variant::Codex(api::harness::Codex {}),
_ => return None,
};
Some(api::Harness {
variant: Some(variant),
})
}
impl TryFrom<RunAgentsResult> for api::request::input::tool_call_result::Result {
type Error = ConvertToAPITypeError;
fn try_from(result: RunAgentsResult) -> Result<Self, Self::Error> {
match result {
RunAgentsResult::Launched {
model_id,
harness_type,
execution_mode,
agents,
} => Ok(
api::request::input::tool_call_result::Result::RunAgentsResult(
api::RunAgentsResult {
outcome: Some(api::run_agents_result::Outcome::Launched(
api::run_agents_result::Launched {
resolved_model_id: model_id,
resolved_harness: build_api_harness(&harness_type),
resolved_execution_mode: Some(execution_mode.into()),
agents: agents.into_iter().map(Into::into).collect(),
},
)),
},
),
),
RunAgentsResult::Denied { reason } => Ok(
api::request::input::tool_call_result::Result::RunAgentsResult(
api::RunAgentsResult {
outcome: Some(api::run_agents_result::Outcome::Denied(
api::run_agents_result::Denied { reason },
)),
},
),
),
RunAgentsResult::Failure { error } => Ok(
api::request::input::tool_call_result::Result::RunAgentsResult(
api::RunAgentsResult {
outcome: Some(api::run_agents_result::Outcome::Failure(
api::run_agents_result::Failure { error },
)),
},
),
),
// Reject is conveyed by the generic ToolCallResult.Cancel marker
// synthesized server-side on the next user input; nothing for the
// client to send on the wire here.
RunAgentsResult::Cancelled => Err(ConvertToAPITypeError::Ignore),
}
}
}
impl TryFrom<InsertReviewCommentsResult> for api::request::input::tool_call_result::Result {
type Error = ConvertToAPITypeError;
@@ -1504,6 +1670,22 @@ impl TryFrom<InsertReviewCommentsResult> for api::request::input::tool_call_resu
}
}
impl TryFrom<WaitForEventsResult> for api::request::input::tool_call_result::Result {
type Error = ConvertToAPITypeError;
/// Completed → wire form; Cancelled → drop (mirrors RunAgents).
fn try_from(result: WaitForEventsResult) -> Result<Self, Self::Error> {
match result {
WaitForEventsResult::Completed => Ok(
api::request::input::tool_call_result::Result::WaitForEvents(
api::WaitForEventsResult {},
),
),
WaitForEventsResult::Cancelled => Err(ConvertToAPITypeError::Ignore),
}
}
}
#[cfg(test)]
#[path = "convert_tests.rs"]
mod tests;
+142 -9
View File
@@ -1,17 +1,16 @@
mod convert;
use std::{fmt::Display, ops::Range, time::SystemTime};
use std::fmt::Display;
use std::ops::Range;
use std::time::SystemTime;
use galaxy_core::command::ExitCode;
use galaxy_terminal::model::BlockId;
use chrono::{DateTime, Local};
use itertools::Itertools as _;
use serde::{Deserialize, Serialize};
use warp_multi_agent_api::apply_file_diffs_result::success::UpdatedFileContent;
use crate::{
agent::FileLocations,
document::{AIDocumentId, AIDocumentVersion},
};
use crate::agent::FileLocations;
use crate::document::{AIDocumentId, AIDocumentVersion};
#[derive(Debug, Clone, PartialEq)]
pub enum AIAgentActionResultType {
@@ -95,6 +94,14 @@ pub enum AIAgentActionResultType {
TransferShellCommandControlToUser(TransferShellCommandControlToUserResult),
/// The result of asking the user a question.
AskUserQuestion(AskUserQuestionResult),
/// The result of an orchestrate tool call: launched (with per-agent
/// outcomes), launch denied (Stage 2), failure, or cancelled.
RunAgents(RunAgentsResult),
/// Result of the client-side wait_for_events watchdog or inbound
/// resume.
WaitForEvents(WaitForEventsResult),
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
@@ -161,6 +168,8 @@ impl Display for AIAgentActionResultType {
AIAgentActionResultType::SendMessageToAgent(result) => result.fmt(f),
AIAgentActionResultType::TransferShellCommandControlToUser(result) => result.fmt(f),
AIAgentActionResultType::AskUserQuestion(result) => result.fmt(f),
AIAgentActionResultType::RunAgents(result) => result.fmt(f),
AIAgentActionResultType::WaitForEvents(result) => result.fmt(f),
AIAgentActionResultType::OpenCodeReview | AIAgentActionResultType::InitProject => {
Ok(())
}
@@ -175,6 +184,8 @@ pub enum RequestCommandOutputResult {
command: String,
output: String,
exit_code: ExitCode,
start_ts: Option<DateTime<Local>>,
completed_ts: Option<DateTime<Local>>,
},
LongRunningCommandSnapshot {
block_id: BlockId,
@@ -266,6 +277,8 @@ pub enum WriteToLongRunningShellCommandResult {
block_id: BlockId,
output: String,
exit_code: ExitCode,
start_ts: Option<DateTime<Local>>,
completed_ts: Option<DateTime<Local>>,
},
Cancelled,
Error(ShellCommandError),
@@ -555,6 +568,8 @@ pub enum ReadShellCommandOutputResult {
block_id: BlockId,
output: String,
exit_code: ExitCode,
start_ts: Option<DateTime<Local>>,
completed_ts: Option<DateTime<Local>>,
},
LongRunningCommandSnapshot {
command: String,
@@ -763,6 +778,12 @@ impl AIAgentActionResultType {
AIAgentActionResultType::AskUserQuestion(_) => {
"The user's answers to clarifying questions"
}
AIAgentActionResultType::RunAgents(_) => {
"The result of an orchestrate batch of child agents"
}
AIAgentActionResultType::WaitForEvents(_) => {
"The local watchdog timed out while waiting for inbound events"
}
}
}
@@ -800,6 +821,8 @@ impl AIAgentActionResultType {
| TransferShellCommandControlToUserResult::CommandFinished { .. },
) => true,
Self::AskUserQuestion(AskUserQuestionResult::Success { .. }) => true,
Self::RunAgents(RunAgentsResult::Launched { .. }) => true,
Self::WaitForEvents(WaitForEventsResult::Completed) => true,
_ => false,
}
}
@@ -828,7 +851,10 @@ impl AIAgentActionResultType {
| Self::AskUserQuestion(AskUserQuestionResult::Error(_))
| Self::TransferShellCommandControlToUser(
TransferShellCommandControlToUserResult::Error(_),
) => true,
)
| Self::RunAgents(RunAgentsResult::Failure { .. } | RunAgentsResult::Denied { .. }) => {
true
}
_ => false,
}
}
@@ -952,7 +978,9 @@ impl AIAgentActionResultType {
| Self::StartAgent(StartAgentResult::Cancelled { .. })
| Self::SendMessageToAgent(SendMessageToAgentResult::Cancelled)
// SkippedByAutoApprove is intentionally excluded: the agent should continue.
| Self::AskUserQuestion(AskUserQuestionResult::Cancelled) => true,
| Self::AskUserQuestion(AskUserQuestionResult::Cancelled)
| Self::RunAgents(RunAgentsResult::Cancelled)
| Self::WaitForEvents(WaitForEventsResult::Cancelled) => true,
_ => false,
}
}
@@ -1234,6 +1262,8 @@ pub enum RequestComputerUseResult {
Approved {
screenshot: computer_use::Screenshot,
platform: computer_use::Platform,
/// The on-screen windows the agent may target.
windows: Vec<computer_use::WindowInfo>,
},
/// Request errored.
Error(String),
@@ -1280,6 +1310,9 @@ impl Display for FetchConversationResult {
}
}
// TODO(QUALITY-788): Delete legacy start_agent/start_agent_v2 result support once
// old preview orchestration history no longer needs parse/display/result compatibility.
// Linear issue: QUALITY-788.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum StartAgentResult {
Success {
@@ -1321,6 +1354,83 @@ impl Display for StartAgentResult {
}
}
/// The terminal outcome of an orchestrate tool call.
///
/// Mirrors the proto `RunAgentsResult` oneof, with an additional
/// `Cancelled` variant used internally by the action machinery when the
/// user clicks Reject. The proto wire form for cancellation is the
/// generic `ToolCallResult.Cancel` marker; the conversion code emits
/// `ConvertToAPITypeError::Ignore` for `Cancelled` so the input
/// interceptor can synthesize the marker on the next outbound input.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum RunAgentsResult {
/// Orchestration launched. Carries the resolved configuration and one
/// `AgentOutcome` per `agent_run_configs[]` entry, in input order.
Launched {
model_id: String,
harness_type: String,
execution_mode: RunAgentsLaunchedExecutionMode,
agents: Vec<RunAgentsAgentOutcome>,
},
/// Declined for a non-error reason (currently disapproval).
Denied { reason: String },
/// Actual error path: server-side validation rejected the call, or the
/// client could not begin the launch sequence at all.
Failure { error: String },
/// User rejected via the Reject button. Wire form is the generic
/// `ToolCallResult.Cancel` marker, synthesized by the server's input
/// interceptor on the next user input.
Cancelled,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum RunAgentsLaunchedExecutionMode {
Local,
Remote {
environment_id: String,
worker_host: String,
computer_use_enabled: bool,
},
}
/// Per-agent outcome reported in `RunAgentsResult::Launched.agents`.
/// Order mirrors the input order of `RunAgents.agent_run_configs[]`,
/// regardless of which `CreateAgentTask` call returned first.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct RunAgentsAgentOutcome {
pub name: String,
pub kind: RunAgentsAgentOutcomeKind,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum RunAgentsAgentOutcomeKind {
Launched { agent_id: String },
Failed { error: String },
}
impl Display for RunAgentsResult {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RunAgentsResult::Launched { agents, .. } => {
let launched = agents
.iter()
.filter(|a| matches!(a.kind, RunAgentsAgentOutcomeKind::Launched { .. }))
.count();
write!(
f,
"Orchestrate launched ({launched}/{} agents started)",
agents.len()
)
}
RunAgentsResult::Denied { reason } => {
write!(f, "Orchestrate launch denied: {reason}")
}
RunAgentsResult::Failure { error } => write!(f, "Orchestrate failure: {error}"),
RunAgentsResult::Cancelled => write!(f, "Orchestrate cancelled"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum SendMessageToAgentResult {
Success { message_id: String },
@@ -1353,6 +1463,8 @@ pub enum TransferShellCommandControlToUserResult {
block_id: BlockId,
output: String,
exit_code: ExitCode,
start_ts: Option<DateTime<Local>>,
completed_ts: Option<DateTime<Local>>,
},
Cancelled,
Error(ShellCommandError),
@@ -1447,3 +1559,24 @@ impl Display for AskUserQuestionResult {
}
}
}
/// Result of a client-side wait_for_events action.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum WaitForEventsResult {
/// Watchdog fired or an inbound resume signal closed the wait. The
/// agent's next turn observes an empty WaitForEvents result on the
/// wire and decides how to proceed.
Completed,
/// User cancelled the conversation while waiting. Mirrors
/// RunAgents::Cancelled: no tool-call result is sent on the wire.
Cancelled,
}
impl Display for WaitForEventsResult {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Completed => write!(f, "Wait for events completed"),
Self::Cancelled => write!(f, "Wait for events cancelled"),
}
}
}