$new Previously unseen ability names. * @param list $modified Abilities whose definition changed. */ public function notify_ability_drift( array $new, array $modified ): void { if ( array() === $new && array() === $modified ) { return; } if ( '1' !== (string) get_option( self::OPTION_EMAIL, '1' ) ) { return; } $throttle_key = 'adaf_anomaly_tool_' . substr( md5( (string) wp_json_encode( array( $new, $modified ) ) ), 0, 20 ); if ( false !== get_transient( $throttle_key ) ) { return; } set_transient( $throttle_key, 1, self::THROTTLE ); $paragraphs = array(); if ( array() !== $new ) { $paragraphs[] = sprintf( /* translators: 1: number of new abilities, 2: comma-separated ability names. */ _n( '%1$d new ability was registered on your site: %2$s. If you did not just install or update a plugin that provides it, investigate before agents can use it.', '%1$d new abilities were registered on your site: %2$s. If you did not just install or update a plugin that provides them, investigate before agents can use them.', count( $new ), 'agentic-daisy-ai-agent-firewall' ), count( $new ), implode( ', ', $new ) ); } if ( array() !== $modified ) { $paragraphs[] = sprintf( /* translators: 1: number of changed abilities, 2: comma-separated ability names. */ _n( 'The definition of %1$d ability changed: %2$s. A benign tool being silently replaced is a known attack (tool poisoning); verify the change is expected.', 'The definitions of %1$d abilities changed: %2$s. A benign tool being silently replaced is a known attack (tool poisoning); verify the changes are expected.', count( $modified ), 'agentic-daisy-ai-agent-firewall' ), count( $modified ), implode( ', ', $modified ) ); } wp_mail( (string) get_option( 'admin_email' ), __( '[Agent Firewall] Ability registry changed', 'agentic-daisy-ai-agent-firewall' ), implode( "\n\n", $paragraphs ) ); } /** * Send the (throttled) alert email. * * @param int $agent_id Agent that exceeded its limit. * @param array $rule The matched limit rule. */ public function notify( int $agent_id, array $rule ): void { if ( '1' !== (string) get_option( self::OPTION_EMAIL, '1' ) ) { return; } $throttle_key = 'adaf_anomaly_mail_' . $agent_id; if ( false !== get_transient( $throttle_key ) ) { return; } set_transient( $throttle_key, 1, self::THROTTLE ); $limit = isset( $rule['limit'] ) && is_array( $rule['limit'] ) ? $rule['limit'] : array(); $writes = isset( $limit['writes'] ) ? (int) $limit['writes'] : 0; $window = isset( $limit['window'] ) ? (int) $limit['window'] : 0; $subject = sprintf( /* translators: %d: agent id. */ __( '[Agent Firewall] Rate limit exceeded by Agent #%d', 'agentic-daisy-ai-agent-firewall' ), $agent_id ); $body = sprintf( /* translators: 1: agent id, 2: allowed writes, 3: window in seconds. */ __( 'Agent #%1$d exceeded its write rate limit (%2$d writes per %3$d seconds) and further writes were denied. Review the Agent Firewall ledger; a runaway loop or a hijacked agent may be at work. Further alerts for this agent are muted for one hour.', 'agentic-daisy-ai-agent-firewall' ), $agent_id, $writes, $window ); wp_mail( (string) get_option( 'admin_email' ), $subject, $body ); } }