revert(agent): drop reply delivery MCP hook

Signed-off-by: Michael Neale <michael.neale@gmail.com>
This commit is contained in:
Michael Neale
2026-07-30 00:24:54 +10:00
parent fcb5e813e9
commit 463b47d673
3 changed files with 1 additions and 146 deletions
+1 -12
View File
@@ -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<todo::HookParams>,
) -> Result<CallToolResult, ErrorData> {
let objections = [
self.todos.stop_objection(),
self.state.reply_delivery.stop_objection(),
]
.into_iter()
.filter(|objection| !objection.is_empty())
.collect::<Vec<_>>();
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
-128
View File
@@ -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<DeliveryStatus>,
}
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<R>(&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::<serde_json::Value>(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"));
}
}
-6
View File
@@ -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<VecDeque<PathBuf>>,
pub reply_delivery: crate::reply_delivery::ReplyDeliveryState,
next_call_id: Mutex<u64>,
}
@@ -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,