|
| 1 | +use agent_client_protocol::{self as acp}; |
| 2 | +use serde_json::json; |
| 3 | +use tokio::time::Duration; |
| 4 | +use tokio::time::sleep; |
| 5 | + |
| 6 | +use crate::MockAgent; |
| 7 | + |
| 8 | +const RUNAWAY_TITLE: &str = "Search runaway-pattern in runaway-search-fixture"; |
| 9 | +const RUNAWAY_CALL_ID: &str = "runaway-search-001"; |
| 10 | + |
| 11 | +pub(crate) async fn run( |
| 12 | + agent: &MockAgent, |
| 13 | + session_id: acp::SessionId, |
| 14 | +) -> Result<acp::PromptResponse, acp::Error> { |
| 15 | + let updates = env_usize("MOCK_AGENT_RUNAWAY_SEARCH_UPDATES", 60); |
| 16 | + let lines_per_update = env_usize("MOCK_AGENT_RUNAWAY_SEARCH_LINES_PER_UPDATE", 25); |
| 17 | + let line_len = env_usize("MOCK_AGENT_RUNAWAY_SEARCH_LINE_LEN", 96); |
| 18 | + let delay_ms = env_u64("MOCK_AGENT_RUNAWAY_SEARCH_DELAY_MS", 2); |
| 19 | + let skip_completion = std::env::var("MOCK_AGENT_RUNAWAY_SEARCH_SKIP_COMPLETION").is_ok(); |
| 20 | + let skip_final_text = std::env::var("MOCK_AGENT_RUNAWAY_SEARCH_SKIP_FINAL_TEXT").is_ok(); |
| 21 | + |
| 22 | + eprintln!( |
| 23 | + "Mock agent: sending runaway search stream updates={updates} lines_per_update={lines_per_update} line_len={line_len} delay_ms={delay_ms}" |
| 24 | + ); |
| 25 | + |
| 26 | + let call_id = acp::ToolCallId::new(RUNAWAY_CALL_ID); |
| 27 | + agent |
| 28 | + .send_tool_call( |
| 29 | + session_id.clone(), |
| 30 | + acp::ToolCall::new(call_id.clone(), RUNAWAY_TITLE) |
| 31 | + .kind(acp::ToolKind::Search) |
| 32 | + .status(acp::ToolCallStatus::Pending) |
| 33 | + .raw_input(json!({ |
| 34 | + "pattern": "runaway-pattern", |
| 35 | + "path": "runaway-search-fixture", |
| 36 | + })), |
| 37 | + ) |
| 38 | + .await?; |
| 39 | + |
| 40 | + let padding = "x".repeat(line_len); |
| 41 | + let mut cumulative_output = String::new(); |
| 42 | + |
| 43 | + for update_index in 0..updates { |
| 44 | + if agent.cancel_requested.get() { |
| 45 | + return Ok(acp::PromptResponse::new(acp::StopReason::Cancelled)); |
| 46 | + } |
| 47 | + |
| 48 | + for line_index in 0..lines_per_update { |
| 49 | + let prefix = format!( |
| 50 | + "/repo/runaway-search-fixture/src/path_{update_index:04}_{line_index:04}.rs:{}: runaway-pattern ", |
| 51 | + update_index * lines_per_update + line_index + 1 |
| 52 | + ); |
| 53 | + cumulative_output.push_str(&prefix); |
| 54 | + let padding_len = line_len.saturating_sub(prefix.len()); |
| 55 | + cumulative_output.push_str(&padding[..padding_len]); |
| 56 | + cumulative_output.push('\n'); |
| 57 | + } |
| 58 | + |
| 59 | + agent |
| 60 | + .send_tool_call_update( |
| 61 | + session_id.clone(), |
| 62 | + acp::ToolCallUpdate::new( |
| 63 | + call_id.clone(), |
| 64 | + acp::ToolCallUpdateFields::new() |
| 65 | + .status(acp::ToolCallStatus::InProgress) |
| 66 | + .content(vec![acp::ToolCallContent::Content(acp::Content::new( |
| 67 | + acp::ContentBlock::Text(acp::TextContent::new( |
| 68 | + cumulative_output.clone(), |
| 69 | + )), |
| 70 | + ))]), |
| 71 | + ), |
| 72 | + ) |
| 73 | + .await?; |
| 74 | + |
| 75 | + if delay_ms > 0 { |
| 76 | + sleep(Duration::from_millis(delay_ms)).await; |
| 77 | + } |
| 78 | + } |
| 79 | + |
| 80 | + if !skip_completion { |
| 81 | + agent |
| 82 | + .send_tool_call_update( |
| 83 | + session_id.clone(), |
| 84 | + acp::ToolCallUpdate::new( |
| 85 | + call_id, |
| 86 | + acp::ToolCallUpdateFields::new().status(acp::ToolCallStatus::Completed), |
| 87 | + ), |
| 88 | + ) |
| 89 | + .await?; |
| 90 | + } |
| 91 | + |
| 92 | + if !skip_final_text { |
| 93 | + agent |
| 94 | + .send_text_chunk(session_id, "Runaway search scenario complete.") |
| 95 | + .await?; |
| 96 | + } |
| 97 | + |
| 98 | + Ok(acp::PromptResponse::new(acp::StopReason::EndTurn)) |
| 99 | +} |
| 100 | + |
| 101 | +fn env_usize(name: &str, default: usize) -> usize { |
| 102 | + std::env::var(name) |
| 103 | + .ok() |
| 104 | + .and_then(|value| value.parse::<usize>().ok()) |
| 105 | + .unwrap_or(default) |
| 106 | +} |
| 107 | + |
| 108 | +fn env_u64(name: &str, default: u64) -> u64 { |
| 109 | + std::env::var(name) |
| 110 | + .ok() |
| 111 | + .and_then(|value| value.parse::<u64>().ok()) |
| 112 | + .unwrap_or(default) |
| 113 | +} |
0 commit comments