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
+324 -23
View File
@@ -1,11 +1,18 @@
use std::{fmt, path::PathBuf};
use std::fmt;
use std::path::PathBuf;
use clap::{Args, Subcommand, ValueEnum};
use serde::{Deserialize, Serialize};
use crate::{
config_file::ConfigFileArgs, environment::EnvironmentCreateArgs, mcp::MCPSpec,
model::ModelArgs, scope::ObjectScope, share::ShareArgs, skill::SkillSpec,
};
use crate::SortOrderArg;
use crate::config_file::ConfigFileArgs;
use crate::environment::EnvironmentCreateArgs;
use crate::json_filter::JsonOutput;
use crate::mcp::MCPSpec;
use crate::model::ModelArgs;
use crate::scope::ObjectScope;
use crate::share::ShareArgs;
use crate::skill::SkillSpec;
/// Output format for agent results.
#[derive(Debug, Copy, Clone, ValueEnum, Eq, PartialEq, Default)]
@@ -119,7 +126,8 @@ impl HiddenComputerUseArgs {
}
}
/// The execution harness for an agent run.
#[derive(Debug, Copy, Clone, ValueEnum, Eq, PartialEq, Default)]
#[derive(Debug, Copy, Clone, ValueEnum, Eq, PartialEq, Hash, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Harness {
/// Use Warp's built-in MAA infrastructure (default).
#[default]
@@ -134,10 +142,14 @@ pub enum Harness {
/// Delegate to the `gemini` CLI.
#[value(name = "gemini")]
Gemini,
/// Delegate to the `codex` CLI.
#[value(name = "codex")]
Codex,
/// A harness produced by a newer client/server that this client doesn't
/// recognize. Surfaced via deserialization fallbacks (e.g. unknown GraphQL
/// enum values, unknown `harness_type` strings); never selectable from the
/// CLI or harness dropdown.
#[serde(other)]
#[value(skip)]
Unknown,
}
@@ -150,7 +162,7 @@ impl Harness {
pub fn parse_local_child_harness(value: &str) -> Option<Self> {
match Self::parse_orchestration_harness(value) {
Some(harness @ (Self::Claude | Self::OpenCode)) => Some(harness),
Some(harness @ (Self::Claude | Self::OpenCode | Self::Codex)) => Some(harness),
Some(Self::Oz) | Some(Self::Gemini) | Some(Self::Unknown) | None => None,
}
}
@@ -161,24 +173,57 @@ impl Harness {
Self::Claude => "Claude Code",
Self::OpenCode => "OpenCode",
Self::Gemini => "Gemini CLI",
Self::Codex => "Codex",
Self::Unknown => "Unknown",
}
}
/// Parses a harness config-name string (the lowercase name written into
/// `HarnessConfig::harness_type` by the spawner, e.g. `"claude"`, `"gemini"`, `"oz"`)
/// into a [`Harness`] variant. Inverse of [`Harness::config_name`]. Returns `None` for
/// unrecognized names so callers can distinguish a future-server harness from a
/// round-tripped [`Harness::Unknown`]; callers that want to fall back to `Unknown`
/// should `.unwrap_or(Harness::Unknown)`. UI surfaces should treat `Unknown` as a
/// non-Oz, non-runnable harness.
pub fn from_config_name(name: &str) -> Option<Self> {
match name {
"oz" => Some(Harness::Oz),
"claude" => Some(Harness::Claude),
"opencode" => Some(Harness::OpenCode),
"gemini" => Some(Harness::Gemini),
"codex" => Some(Harness::Codex),
"unknown" => Some(Harness::Unknown),
_ => None,
}
}
/// Canonical config name for this harness (the lowercase string written into
/// `HarnessConfig::harness_type`). Inverse of [`Harness::from_config_name`].
/// The exhaustive match here forces every new [`Harness`] variant to declare a
/// canonical name, which prevents `from_config_name` from silently falling back to
/// `Unknown` when a new variant is added.
pub fn config_name(self) -> &'static str {
match self {
Harness::Oz => "oz",
Harness::Claude => "claude",
Harness::OpenCode => "opencode",
Harness::Gemini => "gemini",
Harness::Codex => "codex",
Harness::Unknown => "unknown",
}
}
}
impl fmt::Display for Harness {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self {
Harness::Oz => "oz",
Harness::Claude => "claude",
Harness::OpenCode => "opencode",
Harness::Gemini => "gemini",
Harness::Unknown => "unknown",
};
f.write_str(name)
f.write_str(self.config_name())
}
}
#[cfg(test)]
#[path = "agent_tests.rs"]
mod tests;
/// Profile subcommands.
#[derive(Debug, Clone, Subcommand)]
pub enum AgentProfileCommand {
@@ -197,7 +242,33 @@ pub enum AgentCommand {
#[command(subcommand)]
Profile(AgentProfileCommand),
/// List all available agents.
List(ListAgentConfigsArgs),
List(AgentListArgs),
/// Get details of an agent.
Get(AgentGetArgs),
/// Create a new agent.
Create(AgentCreateArgs),
/// Update an existing agent.
Update(AgentUpdateArgs),
/// Delete an agent.
Delete(AgentDeleteArgs),
/// List available agent skills.
Skills(ListAgentSkillsArgs),
}
impl AgentCommand {
pub(crate) fn as_str_for_tracing(&self) -> &'static str {
match self {
AgentCommand::Run(_) => "agent run",
AgentCommand::RunCloud(_) => "agent run-cloud",
AgentCommand::Profile(_) => "agent profile",
AgentCommand::List(_) => "agent list",
AgentCommand::Get(_) => "agent get",
AgentCommand::Create(_) => "agent create",
AgentCommand::Update(_) => "agent update",
AgentCommand::Delete(_) => "agent delete",
AgentCommand::Skills(_) => "agent skills",
}
}
}
#[derive(Debug, Clone, Args)]
@@ -230,8 +301,8 @@ pub struct RunAgentArgs {
///
/// When used with --prompt, the skill provides the base context and the prompt is the task.
///
/// To automate a skill on a schedule, use `oz schedule create --skill <SPEC>`.
#[arg(long = "skill", value_name = "SPEC")]
/// To automate a skill on a schedule, use `oz schedule create --skill <SKILL>`.
#[arg(long = "skill", value_name = "SKILL")]
pub skill: Option<SkillSpec>,
/// Name for this agent task.
@@ -257,6 +328,15 @@ pub struct RunAgentArgs {
/// LEGACY: MCP servers to start before executing the agent, identified by UUID.
#[arg(long = "mcp-server", value_name = "UUID", hide = true)]
pub mcp_servers: Vec<uuid::Uuid>,
/// Fail the run when any requested MCP server fails to start.
///
/// By default, MCP servers that don't start within the startup timeout are
/// skipped and the agent runs without their tools.
#[arg(long = "strict-mcp-startup")]
pub strict_mcp_startup: bool,
/// Maximum time to wait for requested MCP servers to start (e.g. `30s`, `1m`).
#[arg(long = "mcp-startup-timeout", value_name = "DURATION")]
pub mcp_startup_timeout: Option<humantime::Duration>,
/// Cloud environment to use, identified by ID.
#[arg(long = "environment", short = 'e', value_name = "ID")]
pub environment: Option<String>,
@@ -278,6 +358,10 @@ pub struct RunAgentArgs {
#[command(flatten)]
pub snapshot: SnapshotArgs,
/// Identifier for the task that spawned this agent, used to report progress.
///
/// When `--conversation` is omitted, the conversation id is read off the server-side
/// task metadata. Some worker follow-up call sites still pass both flags, so keep
/// accepting the compatibility shape until all producers have been updated.
#[arg(long = "task-id", hide = true, conflicts_with_all = ["prompt", "saved_prompt", "file"])]
pub task_id: Option<String>,
@@ -285,9 +369,25 @@ pub struct RunAgentArgs {
#[arg(long = "sandboxed", hide = true)]
pub sandboxed: bool,
/// IAM role ARN to use for federated AWS Bedrock credentials for this run.
#[arg(long = "bedrock-inference-role", value_name = "ROLE_ARN", hide = true)]
#[arg(
long = "bedrock-inference-role",
value_name = "ROLE_ARN",
requires = "bedrock_role_region",
hide = true
)]
pub bedrock_inference_role: Option<String>,
/// AWS region to use for the STS `AssumeRoleWithWebIdentity` call that
/// mints federated Bedrock credentials. Required together with
/// `--bedrock-inference-role`.
#[arg(
long = "bedrock-role-region",
value_name = "REGION",
requires = "bedrock_inference_role",
hide = true
)]
pub bedrock_role_region: Option<String>,
#[command(flatten)]
pub computer_use: HiddenComputerUseArgs,
@@ -305,6 +405,24 @@ pub struct RunAgentArgs {
/// "claude" delegates to the `claude` CLI.
#[arg(long = "harness", value_name = "HARNESS", default_value_t = Harness::Oz, hide = true)]
pub harness: Harness,
/// Skip the initial LLM turn for this run. Used by the empty-prompt cloud-handoff
/// path so the cloud agent comes up ready for follow-up without hallucinating a
/// response against an empty user message.
///
/// Requires `--idle-on-complete` to also be set: with the initial turn skipped, the
/// driver has nothing to drive a completion event, so the process would exit
/// immediately on success without an idle window for the user's follow-up to arrive.
#[arg(
long = "skip-initial-turn",
hide = true,
requires_all = ["task_id", "idle_on_complete"],
conflicts_with_all = ["prompt", "saved_prompt", "file"]
)]
pub skip_initial_turn: bool,
#[arg(long = "configure-git-credentials-with-github", hide = true, requires_all = ["task_id"])]
pub configure_git_credentials_with_github: bool,
}
impl RunAgentArgs {
@@ -363,8 +481,8 @@ pub struct RunCloudArgs {
///
/// When used with --prompt, the skill provides the base context and the prompt is the task.
///
/// To automate a skill on a schedule, use `oz schedule create --skill <SPEC>`.
#[arg(long = "skill", value_name = "SPEC")]
/// To automate a skill on a schedule, use `oz schedule create --skill <SKILL>`.
#[arg(long = "skill", value_name = "SKILL")]
pub skill: Option<SkillSpec>,
/// Name for this agent task.
@@ -384,6 +502,12 @@ pub struct RunCloudArgs {
/// The environment to run this ambient agent in.
#[command(flatten)]
pub environment: EnvironmentCreateArgs,
/// Runner to use for this agent's compute (docker image, instance size,
/// setup commands), identified by ID. Overrides the environment's default runner.
#[arg(long = "runner", value_name = "ID")]
pub runner: Option<String>,
/// Open the agent's session in Warp once it's available.
#[arg(long = "open")]
pub open: bool,
@@ -395,6 +519,14 @@ pub struct RunCloudArgs {
#[command(flatten)]
pub scope: ObjectScope,
/// UID of the agent to execute this run as.
///
/// This will apply the agent's configuration, such
/// as its skills and base model, and attribute
/// credit usage back to the agent.
#[arg(long = "agent", value_name = "UID")]
pub agent_uid: Option<String>,
/// Where this job should be hosted. Setting "warp" runs it on Warp's infrastructure. Any other
/// value is treated is a self-hosted job and the value will be matched with the self-hosted
/// worker's name.
@@ -435,9 +567,178 @@ pub struct RunCloudArgs {
pub claude_auth_secret: Option<String>,
}
/// Arguments for listing available agents.
/// Sort field for named agents.
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum AgentSortByArg {
#[value(name = "name")]
Name,
#[value(name = "created-at")]
CreatedAt,
}
/// Arguments for listing named agents.
#[derive(Debug, Clone, Args)]
pub struct ListAgentConfigsArgs {
pub struct AgentListArgs {
/// Sort field. Only supported for pretty, text, and ndjson output.
#[arg(long = "sort-by", value_enum, value_name = "FIELD")]
pub sort_by: Option<AgentSortByArg>,
/// Sort direction. Only supported for pretty, text, and ndjson output.
#[arg(long = "sort-order", value_enum, value_name = "DIR")]
pub sort_order: Option<SortOrderArg>,
/// JSON formatting configuration.
#[command(flatten)]
pub json_output: JsonOutput,
}
/// Arguments for getting a named agent.
#[derive(Debug, Clone, Args)]
pub struct AgentGetArgs {
/// UID of the agent to get.
pub uid: String,
/// JSON formatting configuration.
#[command(flatten)]
pub json_output: JsonOutput,
}
/// Arguments for creating a named agent.
#[derive(Debug, Clone, Args)]
pub struct AgentCreateArgs {
/// Name of the agent.
#[arg(long = "name", short = 'n')]
pub name: String,
/// Description of the agent.
#[arg(long = "description")]
pub description: Option<String>,
/// Attach a secret to the agent. Repeat the flag for multiple secrets.
#[arg(long = "secret", value_name = "NAME")]
pub secrets: Vec<String>,
/// Attach a skill to the agent. Repeat the flag for multiple skills.
#[arg(long = "skill", value_name = "SKILL")]
pub skills: Vec<String>,
/// Base model for runs of this agent.
#[arg(long = "base-model", value_name = "MODEL_ID")]
pub base_model: Option<String>,
/// Default cloud environment for runs of this agent.
#[arg(long = "environment", short = 'e', value_name = "ENVIRONMENT_ID")]
pub environment: Option<String>,
/// JSON formatting configuration.
#[command(flatten)]
pub json_output: JsonOutput,
}
/// Arguments for updating a named agent.
#[derive(Debug, Clone, Args)]
pub struct AgentUpdateArgs {
/// UID of the agent to update.
pub uid: String,
/// New name for the agent.
#[arg(long = "name", short = 'n')]
pub name: Option<String>,
/// Replacement description for the agent.
#[arg(long = "description", conflicts_with = "remove_description")]
pub description: Option<String>,
/// Remove the agent description.
#[arg(long = "remove-description", conflicts_with = "description")]
pub remove_description: bool,
/// Add a secret to the agent. Repeat the flag for multiple secrets.
#[arg(
long = "add-secret",
value_name = "NAME",
conflicts_with = "remove_all_secrets"
)]
pub add_secrets: Vec<String>,
/// Remove a secret from the agent. Repeat the flag for multiple secrets.
#[arg(
long = "remove-secret",
value_name = "NAME",
conflicts_with = "remove_all_secrets"
)]
pub remove_secrets: Vec<String>,
/// Remove all secrets from the agent.
#[arg(
long = "remove-all-secrets",
conflicts_with_all = ["add_secrets", "remove_secrets"]
)]
pub remove_all_secrets: bool,
/// Add a skill to the agent. Repeat the flag for multiple skills.
#[arg(
long = "add-skill",
value_name = "SKILL",
conflicts_with = "remove_all_skills"
)]
pub add_skills: Vec<String>,
/// Remove a skill from the agent. Repeat the flag for multiple skills.
#[arg(
long = "remove-skill",
value_name = "SKILL",
conflicts_with = "remove_all_skills"
)]
pub remove_skills: Vec<String>,
/// Remove all skills from the agent.
#[arg(
long = "remove-all-skills",
conflicts_with_all = ["add_skills", "remove_skills"]
)]
pub remove_all_skills: bool,
/// Replacement base model for runs executed by this agent.
#[arg(
long = "base-model",
value_name = "MODEL_ID",
conflicts_with = "remove_base_model"
)]
pub base_model: Option<String>,
/// Remove the agent base model.
#[arg(long = "remove-base-model", conflicts_with = "base_model")]
pub remove_base_model: bool,
/// Replacement default cloud environment for runs executed by this agent.
#[arg(
long = "environment",
short = 'e',
value_name = "ENVIRONMENT_ID",
conflicts_with = "remove_environment"
)]
pub environment: Option<String>,
/// Remove the agent default environment.
#[arg(long = "remove-environment", conflicts_with = "environment")]
pub remove_environment: bool,
/// JSON formatting configuration.
#[command(flatten)]
pub json_output: JsonOutput,
}
/// Arguments for deleting a named agent.
#[derive(Debug, Clone, Args)]
pub struct AgentDeleteArgs {
/// UID of the agent to delete.
pub uid: String,
}
/// Arguments for listing available agent skills.
#[derive(Debug, Clone, Args)]
pub struct ListAgentSkillsArgs {
/// List skills from a specific GitHub repository.
///
/// Format: `owner/repo` or `https://github.com/owner/repo`