diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 4481d334b27b82b5c6748247b5598e77b6d077d8..bed7b46cd058197c83135b8786c37fa51e5aef13 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -32,6 +32,7 @@ set(llcommon_SOURCE_FILES
     llapp.cpp
     llapr.cpp
     llassettype.cpp
+    llavatarname.cpp
     llbase32.cpp
     llbase64.cpp
     llcommon.cpp
@@ -113,6 +114,7 @@ set(llcommon_HEADER_FILES
     llallocator.h
     llallocator_heap_profile.h
     llagentconstants.h
+    llavatarname.h
     llapp.h
     llapr.h
     llassettype.h
diff --git a/indra/llcommon/llavatarname.cpp b/indra/llcommon/llavatarname.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..056337478582d97d4735f313aa12c193efa6e6f5
--- /dev/null
+++ b/indra/llcommon/llavatarname.cpp
@@ -0,0 +1,44 @@
+/** 
+ * @file llavatarname.cpp
+ * @brief Represents name-related data for an avatar, such as the
+ * username/SLID ("bobsmith123" or "james.linden") and the display
+ * name ("James Cook")
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ * 
+ * Copyright (c) 2010, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+#include "linden_common.h"
+
+#include "llavatarname.h"
+
+bool LLAvatarName::operator<(const LLAvatarName& rhs) const
+{
+	if (mSLID == rhs.mSLID)
+		return mDisplayName < rhs.mDisplayName;
+	else
+		return mSLID < rhs.mSLID;
+}
diff --git a/indra/llcommon/llavatarname.h b/indra/llcommon/llavatarname.h
new file mode 100644
index 0000000000000000000000000000000000000000..4264a8e6552199477f5e01486fcb681740bc0b0f
--- /dev/null
+++ b/indra/llcommon/llavatarname.h
@@ -0,0 +1,53 @@
+/** 
+ * @file llavatarname.h
+ * @brief Represents name-related data for an avatar, such as the
+ * username/SLID ("bobsmith123" or "james.linden") and the display
+ * name ("James Cook")
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ * 
+ * Copyright (c) 2010, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+#ifndef LLAVATARNAME_H
+#define LLAVATARNAME_H
+
+#include <string>
+
+class LL_COMMON_API LLAvatarName
+{
+public:
+	bool operator<(const LLAvatarName& rhs) const;
+
+	// "bobsmith123" or "james.linden", US-ASCII only
+	std::string mSLID;
+
+	// "Jose' Sanchez" or "James Linden", UTF-8 encoded Unicode
+	// Contains data whether or not user has explicitly set
+	// a display name; may duplicate their SLID.
+	std::string mDisplayName;
+};
+
+#endif
diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt
index 1f8ee2671663f9fdd69aaa724da1e845e6d54925..0d07015f2412e62519366c7c5334ed6368c4d170 100644
--- a/indra/llmessage/CMakeLists.txt
+++ b/indra/llmessage/CMakeLists.txt
@@ -25,6 +25,7 @@ set(llmessage_SOURCE_FILES
     llares.cpp
     llareslistener.cpp
     llassetstorage.cpp
+    llavatarnamecache.cpp
     llblowfishcipher.cpp
     llbuffer.cpp
     llbufferstream.cpp
@@ -110,6 +111,7 @@ set(llmessage_HEADER_FILES
     llares.h
     llareslistener.h
     llassetstorage.h
+    llavatarnamecache.h
     llblowfishcipher.h
     llbuffer.h
     llbufferstream.h
diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d837019e11ddd0dc50e4de72fa83438b7305b617
--- /dev/null
+++ b/indra/llmessage/llavatarnamecache.cpp
@@ -0,0 +1,99 @@
+/** 
+ * @file llavatarnamecache.cpp
+ * @brief Provides lookup of avatar SLIDs ("bobsmith123") and display names
+ * ("James Cook") from avatar UUIDs.
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ * 
+ * Copyright (c) 2010, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+#include "linden_common.h"
+
+#include "llavatarnamecache.h"
+
+#include "llcachename.h"	// until we get our own web service
+
+#include <cctype>	// tolower()
+
+static std::string slid_from_full_name(const std::string& full_name)
+{
+	std::string id = full_name;
+	std::string::size_type end = id.length();
+	for (std::string::size_type i = 0; i < end; ++i)
+	{
+		if (id[i] == ' ')
+			id[i] = '.';
+		else
+			id[i] = tolower(id[i]);
+	}
+	return id;
+}
+
+void LLAvatarNameCache::initClass()
+{
+}
+
+void LLAvatarNameCache::cleanupClass()
+{
+}
+
+void LLAvatarNameCache::importFile(std::istream& istr)
+{
+}
+
+void LLAvatarNameCache::exportFile(std::ostream& ostr)
+{
+}
+
+void LLAvatarNameCache::idle()
+{
+}
+
+bool LLAvatarNameCache::get(const LLUUID& agent_id, LLAvatarName *av_name)
+{
+	std::string full_name;
+	bool found = gCacheName->getFullName(agent_id, full_name);
+	if (found)
+	{
+		av_name->mSLID = slid_from_full_name(full_name);
+
+		std::string display_name;
+		if (gCacheName->getDisplayName(agent_id, display_name))
+		{
+			av_name->mDisplayName = display_name;
+		}
+		else
+		{
+			// ...no explicit display name, use legacy name
+			av_name->mDisplayName = full_name;
+		}
+	}
+	return found;
+}
+
+void LLAvatarNameCache::get(const LLUUID& agent_id, name_cache_callback_t callback)
+{
+}
diff --git a/indra/llmessage/llavatarnamecache.h b/indra/llmessage/llavatarnamecache.h
new file mode 100644
index 0000000000000000000000000000000000000000..adf12bbbc66e8fd0e4109f5ea1c4b1feb83dd4f9
--- /dev/null
+++ b/indra/llmessage/llavatarnamecache.h
@@ -0,0 +1,58 @@
+/** 
+ * @file llavatarnamecache.h
+ * @brief Provides lookup of avatar SLIDs ("bobsmith123") and display names
+ * ("James Cook") from avatar UUIDs.
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ * 
+ * Copyright (c) 2010, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+#ifndef LLAVATARNAMECACHE_H
+#define LLAVATARNAMECACHE_H
+
+#include "llavatarname.h"	// for convenience
+
+namespace LLAvatarNameCache
+{
+	void initClass();
+	void cleanupClass();
+
+	void importFile(std::istream& istr);
+	void exportFile(std::ostream& ostr);
+
+	void idle();
+
+	// If name is in cache, returns true and fills in provided LLAvatarName
+	// otherwise returns false
+	bool get(const LLUUID& agent_id, LLAvatarName *av_name);
+
+	// Fetches name information and calls callback.
+	// If name information is in cache, callback will be called immediately.
+	typedef void (*name_cache_callback_t)(const LLUUID& agent_id, const LLAvatarName& av_name);
+	void get(const LLUUID& agent_id, name_cache_callback_t callback);
+}
+
+#endif
diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp
index 0b68b66ff909298a8aff70f1e0c1e36e2a23d96c..dae2c44f0839449bdfb3c10c9b99ce5e15eceb81 100644
--- a/indra/llui/llurlentry.cpp
+++ b/indra/llui/llurlentry.cpp
@@ -35,6 +35,7 @@
 #include "llurlentry.h"
 #include "lluri.h"
 
+#include "llavatarnamecache.h"
 #include "llcachename.h"
 #include "lltrans.h"
 #include "lluicolortable.h"
@@ -312,26 +313,19 @@ LLUrlEntryAgent::LLUrlEntryAgent()
 }
 
 // IDEVO demo code
-std::string LLUrlEntryAgent::buildName(const LLUUID& id, const std::string& full_name)
+std::string LLUrlEntryAgent::buildName(const LLUUID& id)
 {
-	std::string final;
-	std::string display_name;
-	if (gCacheName->getDisplayName(id, display_name))
-	{
-		final = display_name + " (" + full_name + ")";
-	}
-	else
-	{
-		final = full_name;
-	}
-	return final;
+	// JAMESDEBUG HACK: assume name is there
+	LLAvatarName av_name;
+	LLAvatarNameCache::get(id, &av_name);
+	return av_name.mDisplayName + " (" + av_name.mSLID + ")";
 }
 
 void LLUrlEntryAgent::onNameCache(const LLUUID& id,
 								  const std::string& full_name,
 								  bool is_group)
 {
-	std::string final = buildName(id, full_name);
+	std::string final = buildName(id);
 	// received the agent name from the server - tell our observers
 	callObservers(id.asString(), final);
 }
@@ -359,7 +353,7 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa
 	}
 	else if (gCacheName->getFullName(agent_id, full_name))
 	{
-		return buildName(agent_id, full_name);
+		return buildName(agent_id);
 	}
 	else
 	{
diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h
index 77802957a34d6f25a2a00064b2adace10667c85c..2bc21eb989603aa69dfd31c406bf167b8e2fcdc5 100644
--- a/indra/llui/llurlentry.h
+++ b/indra/llui/llurlentry.h
@@ -168,9 +168,10 @@ class LLUrlEntryAgent : public LLUrlEntryBase
 public:
 	LLUrlEntryAgent();
 	/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+
 private:
 	void onNameCache(const LLUUID& id, const std::string& full_name, bool is_group);
-	std::string buildName(const LLUUID& id, const std::string& full_name);
+	std::string buildName(const LLUUID& id);
 };
 
 ///
diff --git a/indra/llui/tests/llurlentry_stub.cpp b/indra/llui/tests/llurlentry_stub.cpp
index 35c49fc37f4670d9e5c63b72e59bb74257c4d5dd..016969451eb4176f32f49612b6ba003ce289d328 100644
--- a/indra/llui/tests/llurlentry_stub.cpp
+++ b/indra/llui/tests/llurlentry_stub.cpp
@@ -22,11 +22,18 @@
 
 #include "llstring.h"
 #include "llfile.h"
+#include "llavatarnamecache.h"
 #include "llcachename.h"
 #include "lluuid.h"
 
 #include <string>
 
+// Stub for LLAvatarNameCache
+bool LLAvatarNameCache::get(const LLUUID& agent_id, LLAvatarName *av_name)
+{
+	return false;
+}
+
 //
 // Stub implementation for LLCacheName
 //
diff --git a/indra/newview/llinspectavatar.cpp b/indra/newview/llinspectavatar.cpp
index 41dbeab5a762979e717e4b73497cbda830d640e3..6dddc133c2c7f4591e171611b74fe820c8b7ffaf 100644
--- a/indra/newview/llinspectavatar.cpp
+++ b/indra/newview/llinspectavatar.cpp
@@ -37,6 +37,7 @@
 #include "llagent.h"
 #include "llagentdata.h"
 #include "llavataractions.h"
+#include "llavatarnamecache.h"
 #include "llavatarpropertiesprocessor.h"
 #include "llcallingcard.h"
 #include "lldateutil.h"
@@ -603,24 +604,25 @@ void LLInspectAvatar::onVolumeChange(const LLSD& data)
 
 void LLInspectAvatar::onNameCache(
 	const LLUUID& id,
-	const std::string& name,
+	const std::string& full_name,
 	bool is_group)
 {
 	if (id == mAvatarID)
 	{
-		mAvatarName = name;
+		mAvatarName = full_name;
 
 		// IDEVO JAMESDEBUG - need to always display a display name
-		std::string display_name;
-		if (gCacheName->getDisplayName(mAvatarID, display_name))
+		LLAvatarName av_name;
+		if (LLAvatarNameCache::get(mAvatarID, &av_name))
 		{
-			getChild<LLUICtrl>("user_name")->setValue(display_name);
+			getChild<LLUICtrl>("user_name")->setValue(av_name.mDisplayName);
+			getChild<LLUICtrl>("user_slid")->setValue(av_name.mSLID);
 		}
 		else
 		{
-			getChild<LLUICtrl>("user_name")->setValue(name);
+			getChild<LLUICtrl>("user_name")->setValue(full_name);
+			getChild<LLUICtrl>("user_slid")->setValue(full_name);
 		}
-		getChild<LLUICtrl>("user_slid")->setValue(name);
 	}
 }
 
diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp
index 4a61bed475bc4a865fc2df567c4af29443e7c870..9480ca0fdf1cdd5ec74b93628c177d1639fd21d7 100644
--- a/indra/newview/lltoolpie.cpp
+++ b/indra/newview/lltoolpie.cpp
@@ -39,6 +39,7 @@
 #include "llparcel.h"
 
 #include "llagent.h"
+#include "llavatarnamecache.h"
 #include "llviewercontrol.h"
 #include "llfocusmgr.h"
 //#include "llfirstuse.h"
@@ -871,10 +872,10 @@ BOOL LLToolPie::handleTooltipObject( LLViewerObject* hover_object, std::string l
 					full_name = LLTrans::getString("TooltipPerson");
 				}
 			}
-			std::string display_name;
-			if (gCacheName->getDisplayName(hover_object->getID(), display_name))
+			LLAvatarName av_name;
+			if (LLAvatarNameCache::get(hover_object->getID(), &av_name))
 			{
-				final_name = display_name + " (" + full_name + ")";
+				final_name = av_name.mDisplayName + " (" + av_name.mSLID + ")";
 			}
 			else
 			{
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 265adb3e7f2faa7567091946b9a95798c5655af9..e4de75d173f065f23ba5c6550792f1cd334821e4 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -51,6 +51,7 @@
 #include "llagent.h" //  Get state values from here
 #include "llagentwearables.h"
 #include "llanimationstates.h"
+#include "llavatarnamecache.h"
 #include "llavatarpropertiesprocessor.h"
 #include "llviewercontrol.h"
 #include "llcallingcard.h"		// IDEVO for LLAvatarTracker
@@ -2804,10 +2805,10 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last)
 			line += "\n";
 		}
 
-		std::string display_name;
-		if (gCacheName->getDisplayName(getID(), display_name))
+		LLAvatarName av_name;
+		if (LLAvatarNameCache::get(getID(), &av_name))
 		{
-			line += display_name;
+			line += av_name.mDisplayName;
 		}
 		else
 		{