diff --git a/crates/buzz-core/src/agent_turn_metric.rs b/crates/buzz-core/src/agent_turn_metric.rs index 83565c4cd..8344fca89 100644 --- a/crates/buzz-core/src/agent_turn_metric.rs +++ b/crates/buzz-core/src/agent_turn_metric.rs @@ -9,9 +9,7 @@ use nostr::{Event, Keys, PublicKey}; use serde::{Deserialize, Serialize}; -use crate::observer::{ - decrypt_observer_payload, encrypt_observer_payload, ObserverPayloadError, -}; +use crate::observer::{decrypt_observer_payload, encrypt_observer_payload, ObserverPayloadError}; // Re-export for callers that only need the error type. pub use crate::observer::ObserverPayloadError as AgentTurnMetricError; @@ -208,8 +206,7 @@ mod tests { .sign_with_keys(&agent_keys) .expect("sign"); - let decoded = - decrypt_agent_turn_metric(&owner_keys, &event).expect("decrypt"); + let decoded = decrypt_agent_turn_metric(&owner_keys, &event).expect("decrypt"); assert_eq!(decoded, payload); } @@ -239,9 +236,11 @@ mod tests { #[test] fn delta_reliable_defaults_to_true_when_absent() { let json = r#"{"harness":"goose","timestamp":"2026-07-01T20:11:03Z"}"#; - let payload: AgentTurnMetricPayload = - serde_json::from_str(json).expect("parse"); - assert!(payload.delta_reliable, "deltaReliable should default to true"); + let payload: AgentTurnMetricPayload = serde_json::from_str(json).expect("parse"); + assert!( + payload.delta_reliable, + "deltaReliable should default to true" + ); } #[test] diff --git a/crates/buzz-core/src/lib.rs b/crates/buzz-core/src/lib.rs index 7c3a1c38d..6139ee3ae 100644 --- a/crates/buzz-core/src/lib.rs +++ b/crates/buzz-core/src/lib.rs @@ -5,6 +5,8 @@ //! Provides [`StoredEvent`], filter matching, kind constants, and event //! verification. All other Buzz crates depend on this one. +/// NIP-AM: Agent Turn Metric — payload type and encrypt/decrypt helpers. +pub mod agent_turn_metric; /// Channel and membership enums shared across crates. pub mod channel; /// NIP-AE Agent Engrams — slug grammar, conversation key, d-tag derivation, @@ -24,8 +26,6 @@ pub mod kind; pub mod network; /// Agent observer frame helpers. pub mod observer; -/// NIP-AM: Agent Turn Metric — payload type and encrypt/decrypt helpers. -pub mod agent_turn_metric; /// NIP-AB device pairing — crypto primitives, message types, and errors. pub mod pairing; /// Presence status types shared across crates. diff --git a/crates/buzz-relay/src/handlers/ingest.rs b/crates/buzz-relay/src/handlers/ingest.rs index 38e6ed95e..6c7e91fb7 100644 --- a/crates/buzz-relay/src/handlers/ingest.rs +++ b/crates/buzz-relay/src/handlers/ingest.rs @@ -13,9 +13,8 @@ use buzz_auth::Scope; use buzz_core::kind::{ event_kind_u32, is_identity_archive_request_kind, is_parameterized_replaceable, is_relay_admin_kind, KIND_AGENT_ENGRAM, KIND_AGENT_PROFILE, KIND_AGENT_TURN_METRIC, - KIND_APPROVAL_DENY, - KIND_APPROVAL_GRANT, KIND_AUTH, KIND_BOOKMARK_LIST, KIND_BOOKMARK_SET, KIND_CANVAS, - KIND_CONTACT_LIST, KIND_DELETION, KIND_DM_ADD_MEMBER, KIND_DM_HIDE, KIND_DM_OPEN, + KIND_APPROVAL_DENY, KIND_APPROVAL_GRANT, KIND_AUTH, KIND_BOOKMARK_LIST, KIND_BOOKMARK_SET, + KIND_CANVAS, KIND_CONTACT_LIST, KIND_DELETION, KIND_DM_ADD_MEMBER, KIND_DM_HIDE, KIND_DM_OPEN, KIND_EMOJI_LIST, KIND_EMOJI_SET, KIND_EVENT_REMINDER, KIND_FOLLOW_SET, KIND_FORUM_COMMENT, KIND_FORUM_POST, KIND_FORUM_VOTE, KIND_GIFT_WRAP, KIND_GIT_ISSUE, KIND_GIT_PATCH, KIND_GIT_PR_UPDATE, KIND_GIT_PULL_REQUEST, KIND_GIT_REPO_ANNOUNCEMENT, KIND_GIT_REPO_STATE, @@ -1104,7 +1103,11 @@ fn validate_agent_turn_metric_envelope(event: &nostr::Event) -> Result<(), Strin )); } let p = p_tags[0]; - if p.len() != 64 || !p.bytes().all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase()) { + if p.len() != 64 + || !p + .bytes() + .all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase()) + { return Err("agent-turn-metric `p` tag must be 64 lowercase hex chars".to_string()); } @@ -1116,14 +1119,14 @@ fn validate_agent_turn_metric_envelope(event: &nostr::Event) -> Result<(), Strin } let agent = agent_tags[0]; if agent.len() != 64 - || !agent.bytes().all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase()) + || !agent + .bytes() + .all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase()) { return Err("agent-turn-metric `agent` tag must be 64 lowercase hex chars".to_string()); } if agent != event_pubkey_hex { - return Err( - "agent-turn-metric `agent` tag must equal event pubkey".to_string(), - ); + return Err("agent-turn-metric `agent` tag must equal event pubkey".to_string()); } // Content must look like a NIP-44 v2 ciphertext (length, base64, version prefix). @@ -3122,8 +3125,7 @@ mod tests { fn agent_turn_metric_envelope_rejects_missing_p() { let agent = nostr::Keys::generate(); let agent_hex = agent.public_key().to_hex(); - let ev = - make_agent_turn_metric(&agent, &[&["agent", &agent_hex]], &fake_nip44_v2()); + let ev = make_agent_turn_metric(&agent, &[&["agent", &agent_hex]], &fake_nip44_v2()); let err = validate_agent_turn_metric_envelope(&ev).unwrap_err(); assert!(err.contains("`p` tag"), "got: {err}"); } @@ -3132,8 +3134,7 @@ mod tests { fn agent_turn_metric_envelope_rejects_missing_agent() { let agent = nostr::Keys::generate(); let owner_hex = "b".repeat(64); - let ev = - make_agent_turn_metric(&agent, &[&["p", &owner_hex]], &fake_nip44_v2()); + let ev = make_agent_turn_metric(&agent, &[&["p", &owner_hex]], &fake_nip44_v2()); let err = validate_agent_turn_metric_envelope(&ev).unwrap_err(); assert!(err.contains("`agent` tag"), "got: {err}"); } @@ -3149,10 +3150,7 @@ mod tests { &fake_nip44_v2(), ); let err = validate_agent_turn_metric_envelope(&ev).unwrap_err(); - assert!( - err.contains("equal event pubkey"), - "got: {err}" - ); + assert!(err.contains("equal event pubkey"), "got: {err}"); } #[test] @@ -3167,9 +3165,6 @@ mod tests { ); let err = validate_agent_turn_metric_envelope(&ev).unwrap_err(); // error comes from validate_engram_nip44_content with label replaced - assert!( - err.contains("agent-turn-metric"), - "got: {err}" - ); + assert!(err.contains("agent-turn-metric"), "got: {err}"); } } diff --git a/crates/buzz-relay/src/handlers/req.rs b/crates/buzz-relay/src/handlers/req.rs index 8fd003c97..1b1ce4483 100644 --- a/crates/buzz-relay/src/handlers/req.rs +++ b/crates/buzz-relay/src/handlers/req.rs @@ -1097,9 +1097,10 @@ pub(crate) fn result_gated_count_safe_for_pushdown( authed_pubkey_hex: &str, ) -> bool { let p_tag = nostr::SingleLetterTag::lowercase(nostr::Alphabet::P); - filter.generic_tags.get(&p_tag).is_some_and(|values| { - !values.is_empty() && values.iter().all(|v| v == authed_pubkey_hex) - }) + filter + .generic_tags + .get(&p_tag) + .is_some_and(|values| !values.is_empty() && values.iter().all(|v| v == authed_pubkey_hex)) } /// Returns `true` if the event is an author-only kind and the requester is NOT @@ -1685,15 +1686,17 @@ mod tests { #[test] fn result_gated_explicit_44200_can_match() { - let f = Filter::new() - .kind(nostr::Kind::Custom(buzz_core::kind::KIND_AGENT_TURN_METRIC as u16)); + let f = Filter::new().kind(nostr::Kind::Custom( + buzz_core::kind::KIND_AGENT_TURN_METRIC as u16, + )); assert!(filter_can_match_result_gated_kinds(&f)); } #[test] fn result_gated_explicit_30622_can_match() { - let f = Filter::new() - .kind(nostr::Kind::Custom(buzz_core::kind::KIND_DM_VISIBILITY as u16)); + let f = Filter::new().kind(nostr::Kind::Custom( + buzz_core::kind::KIND_DM_VISIBILITY as u16, + )); assert!(filter_can_match_result_gated_kinds(&f)); } @@ -1708,7 +1711,9 @@ mod tests { let (owner, _agent, _other) = three_pubkeys(); let p_tag = nostr::SingleLetterTag::lowercase(nostr::Alphabet::P); let f = nostr::Filter::new() - .kind(nostr::Kind::Custom(buzz_core::kind::KIND_AGENT_TURN_METRIC as u16)) + .kind(nostr::Kind::Custom( + buzz_core::kind::KIND_AGENT_TURN_METRIC as u16, + )) .custom_tags(p_tag, [owner.clone()]); // Owner querying their own metrics — safe to push down. assert!(result_gated_count_safe_for_pushdown(&f, &owner)); @@ -1719,7 +1724,9 @@ mod tests { let (owner, _agent, other) = three_pubkeys(); let p_tag = nostr::SingleLetterTag::lowercase(nostr::Alphabet::P); let f = nostr::Filter::new() - .kind(nostr::Kind::Custom(buzz_core::kind::KIND_AGENT_TURN_METRIC as u16)) + .kind(nostr::Kind::Custom( + buzz_core::kind::KIND_AGENT_TURN_METRIC as u16, + )) .custom_tags(p_tag, [other.clone()]); // Authenticated as owner but #p is someone else — NOT safe. assert!(!result_gated_count_safe_for_pushdown(&f, &owner)); @@ -1728,8 +1735,9 @@ mod tests { #[test] fn result_gated_safe_pushdown_rejects_when_no_p_tag() { let (owner, _agent, _other) = three_pubkeys(); - let f = nostr::Filter::new() - .kind(nostr::Kind::Custom(buzz_core::kind::KIND_AGENT_TURN_METRIC as u16)); + let f = nostr::Filter::new().kind(nostr::Kind::Custom( + buzz_core::kind::KIND_AGENT_TURN_METRIC as u16, + )); // No #p tag — fallback required. assert!(!result_gated_count_safe_for_pushdown(&f, &owner)); }