Skip to content

Commit 9d2296d

Browse files
bnavettaoz-agent
andauthored
Add agent CLI flag for cloud runs (#9935)
## Summary - Adds `--agent <UID>` to `warp agent run-cloud`. - Sends the value as `agent_identity_uid`, matching the public agent run API request field. - Updates spawn request construction sites and tests for CLI parsing and request serialization. ## Testing - `git --no-pager diff --check` - `cargo nextest run --manifest-path /workspace/warp/Cargo.toml -p warp_cli agent_run_cloud_accepts_agent_flag` - `CARGO_BUILD_JOBS=1 cargo check --manifest-path /workspace/warp/Cargo.toml -p warp --lib` - `CARGO_BUILD_JOBS=1 cargo nextest run --manifest-path /workspace/warp/Cargo.toml -p warp spawn_agent_request_serializes_agent_uid_as_agent_identity_uid` - Not run: `cargo fmt --manifest-path /workspace/warp/Cargo.toml --all --check` (`rustfmt` is not installed in the sandbox toolchain) Co-Authored-By: Oz <oz-agent@warp.dev> _Conversation: https://staging.warp.dev/conversation/e3cb2fc1-34d8-43cf-b8cd-fd894946656a_ _Run: https://oz.staging.warp.dev/runs/019dea24-d12b-719f-9ba4-bb9435d546df_ _This PR was generated with [Oz](https://warp.dev/oz)._ --------- Co-authored-by: Oz <oz-agent@warp.dev>
1 parent 434b50d commit 9d2296d

10 files changed

Lines changed: 74 additions & 1 deletion

File tree

app/src/ai/agent_sdk/ambient.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,7 @@ impl AmbientAgentRunner {
484484
(_, true) => Some(false),
485485
_ => None,
486486
},
487+
agent_identity_uid: args.agent_uid,
487488
skill,
488489
attachments,
489490
interactive: None,

app/src/ai/agent_sdk/mcp_config_tests.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ fn serializes_mcp_servers_as_object_not_string() {
278278
}),
279279
title: None,
280280
team: None,
281+
agent_identity_uid: None,
281282
skill: None,
282283
attachments: vec![],
283284
interactive: None,

app/src/ai/ambient_agents/spawn_tests.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@ async fn poll_retries_transient_429_errors() {
364364
config: None,
365365
title: None,
366366
team: None,
367+
agent_identity_uid: None,
367368
skill: None,
368369
attachments: vec![],
369370
interactive: None,
@@ -430,6 +431,7 @@ async fn poll_fails_on_permanent_http_error() {
430431
config: None,
431432
title: None,
432433
team: None,
434+
agent_identity_uid: None,
433435
skill: None,
434436
attachments: vec![],
435437
interactive: None,
@@ -496,6 +498,7 @@ async fn poll_gives_up_after_max_transient_retries() {
496498
config: None,
497499
title: None,
498500
team: None,
501+
agent_identity_uid: None,
499502
skill: None,
500503
attachments: vec![],
501504
interactive: None,
@@ -557,6 +560,7 @@ async fn poll_stops_on_terminal_failure_like_state() {
557560
config: None,
558561
title: None,
559562
team: None,
563+
agent_identity_uid: None,
560564
skill: None,
561565
attachments: vec![],
562566
interactive: None,
@@ -701,6 +705,7 @@ async fn poll_for_session_join_info_waits_until_link_is_available() {
701705
config: None,
702706
title: None,
703707
team: None,
708+
agent_identity_uid: None,
704709
skill: None,
705710
attachments: vec![],
706711
interactive: None,

app/src/pane_group/pane/terminal_pane.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,6 +1564,7 @@ fn launch_remote_child(
15641564
referenced_attachments: vec![],
15651565
conversation_id: None,
15661566
initial_snapshot_token: None,
1567+
agent_identity_uid: None,
15671568
};
15681569

15691570
new_terminal_view.update(ctx, |terminal_view, ctx| {

app/src/server/server_api/ai.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,9 @@ pub struct SpawnAgentRequest {
213213
pub title: Option<String>,
214214
#[serde(skip_serializing_if = "Option::is_none")]
215215
pub team: Option<bool>,
216+
/// Agent identity UID to use as the execution principal for the run.
217+
#[serde(rename = "agent_identity_uid", skip_serializing_if = "Option::is_none")]
218+
pub agent_identity_uid: Option<String>,
216219
/// Use a Claude-compatible skill as the base prompt.
217220
/// Format: "repo:skill_name" or just "skill_name".
218221
/// The skill is resolved at runtime in the agent environment.

app/src/server/server_api/ai_tests.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use super::{
99
AgentMessageHeader, AgentRunEvent, AgentSource, AmbientAgentTaskState, Artifact,
1010
ArtifactDownloadResponse, ArtifactType, ExecutionLocation, ForkConversationResponse,
1111
ListRunsResponse, ReadAgentMessageResponse, RunFollowupRequest, RunSortBy, RunSortOrder,
12-
TaskListFilter,
12+
SpawnAgentRequest, TaskListFilter, UserQueryMode,
1313
};
1414
use crate::notebooks::NotebookId;
1515

@@ -34,6 +34,34 @@ fn ambient_agent_headers_for_task_overrides_existing_cloud_agent_header() {
3434
);
3535
}
3636

37+
#[test]
38+
fn spawn_agent_request_serializes_agent_uid_as_agent_identity_uid() {
39+
let request = SpawnAgentRequest {
40+
prompt: "hello".to_string(),
41+
mode: UserQueryMode::Normal,
42+
config: None,
43+
title: None,
44+
team: None,
45+
agent_identity_uid: Some("agent_123".to_string()),
46+
skill: None,
47+
attachments: vec![],
48+
interactive: None,
49+
parent_run_id: None,
50+
runtime_skills: vec![],
51+
referenced_attachments: vec![],
52+
conversation_id: None,
53+
initial_snapshot_token: None,
54+
};
55+
56+
let value = serde_json::to_value(&request).unwrap();
57+
58+
assert_eq!(
59+
value.get("agent_identity_uid").and_then(|v| v.as_str()),
60+
Some("agent_123")
61+
);
62+
assert!(value.get("agent_uid").is_none());
63+
}
64+
3765
#[test]
3866
fn test_deserialize_file_artifact_download_response() {
3967
let json = r#"{

app/src/terminal/view/ambient_agent/model.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,7 @@ impl AmbientAgentViewModel {
877877
config,
878878
title: None,
879879
team: None,
880+
agent_identity_uid: None,
880881
skill: None,
881882
attachments,
882883
interactive: None,
@@ -1328,6 +1329,7 @@ impl AmbientAgentViewModel {
13281329
referenced_attachments: vec![],
13291330
conversation_id: Some(forked_conversation_id),
13301331
initial_snapshot_token,
1332+
agent_identity_uid: None,
13311333
};
13321334
self.spawn_agent_with_request(request, ctx);
13331335
}

app/src/terminal/view_tests.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,7 @@ fn cloud_mode_dispatched_agent_inserts_queued_user_query() {
718718
config: None,
719719
title: None,
720720
team: None,
721+
agent_identity_uid: None,
721722
skill: None,
722723
attachments: vec![],
723724
interactive: None,

crates/warp_cli/src/agent.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,14 @@ pub struct RunCloudArgs {
438438
#[command(flatten)]
439439
pub scope: ObjectScope,
440440

441+
/// UID of the agent to execute this run as.
442+
///
443+
/// This will apply the agent's configuration, such
444+
/// as its skills and base model, and attribute
445+
/// credit usage back to the agent.
446+
#[arg(long = "agent", value_name = "UID")]
447+
pub agent_uid: Option<String>,
448+
441449
/// Where this job should be hosted. Setting "warp" runs it on Warp's infrastructure. Any other
442450
/// value is treated is a self-hosted job and the value will be matched with the self-hosted
443451
/// worker's name.

crates/warp_cli/src/lib_tests.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,29 @@ fn agent_run_cloud_accepts_model() {
275275
assert_eq!(run_args.model.model.as_deref(), Some("gpt-4o"));
276276
}
277277

278+
#[test]
279+
fn agent_run_cloud_accepts_agent_flag() {
280+
let args = Args::try_parse_from([
281+
"warp",
282+
"agent",
283+
"run-cloud",
284+
"--prompt",
285+
"hello",
286+
"--agent",
287+
"agent_123",
288+
])
289+
.unwrap();
290+
291+
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
292+
panic!("Expected `warp agent run-cloud` command");
293+
};
294+
let CliCommand::Agent(AgentCommand::RunCloud(run_args)) = boxed_cmd.as_ref() else {
295+
panic!("Expected `warp agent run-cloud` command");
296+
};
297+
298+
assert_eq!(run_args.agent_uid.as_deref(), Some("agent_123"));
299+
}
300+
278301
#[test]
279302
fn agent_run_cloud_accepts_mcp() {
280303
let uuid = uuid::Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();

0 commit comments

Comments
 (0)