From 463b47d6738e109c0f8dc758b561da8f7e0bf110 Mon Sep 17 00:00:00 2001 From: Michael Neale Date: Thu, 30 Jul 2026 00:24:54 +1000 Subject: [PATCH] revert(agent): drop reply delivery MCP hook Signed-off-by: Michael Neale --- crates/buzz-dev-mcp/src/lib.rs | 13 +-- crates/buzz-dev-mcp/src/reply_delivery.rs | 128 ---------------------- crates/buzz-dev-mcp/src/shell.rs | 6 - 3 files changed, 1 insertion(+), 146 deletions(-) delete mode 100644 crates/buzz-dev-mcp/src/reply_delivery.rs diff --git a/crates/buzz-dev-mcp/src/lib.rs b/crates/buzz-dev-mcp/src/lib.rs index 5d617a877..9b9897480 100644 --- a/crates/buzz-dev-mcp/src/lib.rs +++ b/crates/buzz-dev-mcp/src/lib.rs @@ -12,7 +12,6 @@ use std::sync::Arc; mod paths; mod read_file; -mod reply_delivery; mod rg; mod shell; mod shim; @@ -107,17 +106,7 @@ impl DevMcp { &self, Parameters(_): Parameters, ) -> Result { - let objections = [ - self.todos.stop_objection(), - self.state.reply_delivery.stop_objection(), - ] - .into_iter() - .filter(|objection| !objection.is_empty()) - .collect::>(); - if objections.is_empty() { - self.state.reply_delivery.reset_after_allowed_stop(); - } - todo::text_result(objections.join("\n\n")) + todo::text_result(self.todos.stop_objection()) } /// Hook: called by the agent after context compaction/handoff so the diff --git a/crates/buzz-dev-mcp/src/reply_delivery.rs b/crates/buzz-dev-mcp/src/reply_delivery.rs deleted file mode 100644 index 2e892a725..000000000 --- a/crates/buzz-dev-mcp/src/reply_delivery.rs +++ /dev/null @@ -1,128 +0,0 @@ -use std::path::Path; -use std::sync::Mutex; - -const STOP_OBJECTION: &str = "You have not posted your reply to Buzz. Do not describe or promise the send. Call the `shell` MCP tool now and run `buzz messages send`. Set `--channel` to the actual channel UUID from `[Context]` and `--content` to your reply; never use literal placeholder text. Do not end the turn until that command succeeds."; - -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] -enum DeliveryStatus { - #[default] - AwaitingSend, - Sent, -} - -#[derive(Debug, Default)] -pub struct ReplyDeliveryState { - status: Mutex, -} - -impl ReplyDeliveryState { - pub fn record_shell_result(&self, command: &str, exit_code: i32, stdout: &str) { - let is_send = is_buzz_message_send(command); - let accepted = write_was_accepted(stdout); - if exit_code != 0 || !is_send || !accepted { - return; - } - self.with_status(|status| *status = DeliveryStatus::Sent); - } - - pub fn stop_objection(&self) -> String { - self.with_status(|status| match status { - DeliveryStatus::AwaitingSend => STOP_OBJECTION.to_owned(), - DeliveryStatus::Sent => String::new(), - }) - } - - pub fn reset_after_allowed_stop(&self) { - self.with_status(|status| *status = DeliveryStatus::AwaitingSend); - } - - fn with_status(&self, f: impl FnOnce(&mut DeliveryStatus) -> R) -> R { - let mut guard = match self.status.lock() { - Ok(guard) => guard, - Err(poisoned) => poisoned.into_inner(), - }; - f(&mut guard) - } -} - -fn is_buzz_message_send(command: &str) -> bool { - let mut words = command.split_ascii_whitespace(); - let Some(executable) = words.next() else { - return false; - }; - let executable = executable.trim_matches(['\'', '"']); - let is_buzz = Path::new(executable) - .file_name() - .and_then(|name| name.to_str()) - .is_some_and(|name| matches!(name, "buzz" | "buzz.exe")); - is_buzz && words.next() == Some("messages") && words.next() == Some("send") -} - -fn write_was_accepted(stdout: &str) -> bool { - serde_json::from_str::(stdout.trim()) - .ok() - .and_then(|value| value.get("accepted").and_then(serde_json::Value::as_bool)) - == Some(true) -} - -#[cfg(test)] -mod tests { - use super::*; - - const ACCEPTED: &str = r#"{"event_id":"abc","accepted":true,"message":"ok"}"#; - - #[test] - fn successful_buzz_send_satisfies_stop_then_resets() { - let state = ReplyDeliveryState::default(); - assert!(!state.stop_objection().is_empty()); - - state.record_shell_result( - "buzz messages send --channel c --content hello", - 0, - ACCEPTED, - ); - assert!(state.stop_objection().is_empty()); - - state.reset_after_allowed_stop(); - assert!(!state.stop_objection().is_empty()); - } - - #[test] - fn failed_or_rejected_send_does_not_satisfy_stop() { - let state = ReplyDeliveryState::default(); - state.record_shell_result( - "buzz messages send --channel c --content hello", - 1, - ACCEPTED, - ); - state.record_shell_result( - "buzz messages send --channel c --content hello", - 0, - r#"{"accepted":false}"#, - ); - assert!(!state.stop_objection().is_empty()); - } - - #[test] - fn mentioning_send_in_another_command_does_not_satisfy_stop() { - let state = ReplyDeliveryState::default(); - state.record_shell_result("echo buzz messages send", 0, ACCEPTED); - assert!(!state.stop_objection().is_empty()); - } - - #[test] - fn absolute_buzz_path_is_recognized() { - assert!(is_buzz_message_send( - "/Applications/Buzz.app/Contents/MacOS/buzz messages send --channel c --content hi" - )); - } - - #[test] - fn stop_objection_requires_the_shell_tool_and_successful_send() { - assert!(STOP_OBJECTION.contains("`shell` MCP tool")); - assert!(STOP_OBJECTION.contains("`buzz messages send`")); - assert!(STOP_OBJECTION.contains("actual channel UUID")); - assert!(STOP_OBJECTION.contains("never use literal placeholder")); - assert!(STOP_OBJECTION.contains("command succeeds")); - } -} diff --git a/crates/buzz-dev-mcp/src/shell.rs b/crates/buzz-dev-mcp/src/shell.rs index c9faa251c..7aa95b1d8 100644 --- a/crates/buzz-dev-mcp/src/shell.rs +++ b/crates/buzz-dev-mcp/src/shell.rs @@ -33,7 +33,6 @@ pub struct SharedState { /// bootstrap hint and every `run()` call read the SAME resolution — no drift. pub resolved_shell: Result<(PathBuf, String), String>, pub artifacts: Mutex>, - pub reply_delivery: crate::reply_delivery::ReplyDeliveryState, next_call_id: Mutex, } @@ -59,7 +58,6 @@ impl SharedState { bootstrap_instructions, resolved_shell, artifacts: Mutex::new(VecDeque::with_capacity(ARTIFACT_RING_SIZE)), - reply_delivery: crate::reply_delivery::ReplyDeliveryState::default(), next_call_id: Mutex::new(0), }) } @@ -308,10 +306,6 @@ pub async fn run( let (stderr_text, stderr_truncated, stderr_artifact) = finalize_stream(state, id, "stderr", stderr_cap, &mut notes); - state - .reply_delivery - .record_shell_result(&p.command, exit_code, &stdout_text); - let body = serde_json::json!({ "exit_code": exit_code, "stdout": stdout_text,