diff --git a/indra/llfilesystem/CMakeLists.txt b/indra/llfilesystem/CMakeLists.txt
index 9f24f75eabc2116e50e2674983543ceb1e44aeb1..d14f2adb19be7b5ec4d1e519915caaee2183aabc 100644
--- a/indra/llfilesystem/CMakeLists.txt
+++ b/indra/llfilesystem/CMakeLists.txt
@@ -56,6 +56,10 @@ target_link_libraries(llfilesystem
     )
 target_include_directories( llfilesystem  INTERFACE   ${CMAKE_CURRENT_SOURCE_DIR})
 
+if(TARGET al::sentry)
+    target_compile_definitions(llfilesystem PRIVATE AL_SENTRY=1)
+endif()
+
 # Add tests
 if (LL_TESTS)
     include(LLAddBuildTest)
diff --git a/indra/llfilesystem/lldir_linux.cpp b/indra/llfilesystem/lldir_linux.cpp
index 93f2962dcc318ba99d8f0e4694c9ac37a85180fc..2041a091c54a756992b38300ab294b9597d9df36 100644
--- a/indra/llfilesystem/lldir_linux.cpp
+++ b/indra/llfilesystem/lldir_linux.cpp
@@ -88,21 +88,25 @@ LLDir_Linux::LLDir_Linux()
 #ifdef APP_RO_DATA_DIR
     mAppRODataDir = APP_RO_DATA_DIR;
 #else
-    mAppRODataDir = tmp_str;
-#endif
-    std::string::size_type build_dir_pos = mExecutableDir.rfind("/build-linux-");
+    // Determine the location of the App-Read-Only-Data
+    // Try the working directory then the exe's dir.
+#ifndef AL_SENTRY
+    std::string::size_type build_dir_pos = mExecutableDir.rfind(mDirDelimiter + "build-linux-");
     if (build_dir_pos != std::string::npos)
     {
         // ...we're in a dev checkout
-        mSkinBaseDir = mExecutableDir.substr(0, build_dir_pos) + "/indra/newview/skins";
-        LL_INFOS() << "Running in dev checkout with mSkinBaseDir "
-         << mSkinBaseDir << LL_ENDL;
+        mAppRODataDir = add(mExecutableDir.substr(0, build_dir_pos), "indra", "newview");
+        LL_INFOS() << "Running in dev checkout with mAppRODataDir " << mAppRODataDir << LL_ENDL;
     }
     else
+#endif
     {
         // ...normal installation running
-        mSkinBaseDir = mAppRODataDir + mDirDelimiter + "skins";
+        mAppRODataDir = tmp_str;
     }
+#endif
+
+    mSkinBaseDir = add(mAppRODataDir, "skins");
 
     mOSUserDir = getCurrentUserHome(tmp_str);
     mOSUserAppDir = "";
diff --git a/indra/llfilesystem/lldir_win32.cpp b/indra/llfilesystem/lldir_win32.cpp
index 615abb30f442f95435c63e2b4374b22309109d86..faf7d5147dbc6962685ef35a092b444124332d98 100644
--- a/indra/llfilesystem/lldir_win32.cpp
+++ b/indra/llfilesystem/lldir_win32.cpp
@@ -246,7 +246,7 @@ LLDir_Win32::LLDir_Win32()
     }
     else
     {
-        fprintf(stderr, "Couldn't get APP path, assuming current directory!");
+        PRELOG("Couldn't get APP path, assuming current directory!");
         mExecutableDir = mWorkingDir;
         // Assume it's the current directory
     }
@@ -255,8 +255,20 @@ LLDir_Win32::LLDir_Win32()
 
     // Determine the location of the App-Read-Only-Data
     // Try the working directory then the exe's dir.
-    mAppRODataDir = mWorkingDir;
-
+#ifndef AL_SENTRY
+    std::string::size_type build_dir_pos = mExecutableDir.rfind(mDirDelimiter + "build-vc-");
+    if (build_dir_pos != std::string::npos)
+    {
+        // ...we're in a dev checkout
+        mAppRODataDir = add(mExecutableDir.substr(0, build_dir_pos), "indra", "newview");
+        PRELOG("Running in dev checkout with mAppRODataDir " << mAppRODataDir);
+    }
+    else
+#endif
+    {
+        // ...normal installation running
+        mAppRODataDir = mWorkingDir;
+    }
 
 //  if (mExecutableDir.find("indra") == std::string::npos)
 
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 6a75d3494c8aa358a3d4e13746141672e9d93362..ae69a490066c0c9a5b5ba471046ec1f949decb9c 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -400,6 +400,7 @@ set(viewer_SOURCE_FILES
     llhudeffectpointat.cpp
     llhudeffecttrail.cpp
     llhudeffectblob.cpp
+    llhudeffectresetskeleton.cpp
     llhudicon.cpp
     llhudmanager.cpp
     llhudnametag.cpp
@@ -1148,6 +1149,7 @@ set(viewer_HEADER_FILES
     llhudeffectpointat.h
     llhudeffecttrail.h
     llhudeffectblob.h
+    llhudeffectresetskeleton.h
     llhudicon.h
     llhudmanager.h
     llhudnametag.h
diff --git a/indra/newview/app_settings/colorlut/the_perfect_lut.png b/indra/newview/app_settings/colorlut/the_perfect_lut.png
new file mode 100644
index 0000000000000000000000000000000000000000..f8513cd758a7da9e522ab2c0c15dac351aa2572e
Binary files /dev/null and b/indra/newview/app_settings/colorlut/the_perfect_lut.png differ
diff --git a/indra/newview/llhudeffectresetskeleton.cpp b/indra/newview/llhudeffectresetskeleton.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8afa231fc53093f1490c7e6849c2e97e40caefa2
--- /dev/null
+++ b/indra/newview/llhudeffectresetskeleton.cpp
@@ -0,0 +1,207 @@
+/** 
+ * @file llhudeffectresetskeleton.cpp
+ * @brief LLHUDEffectResetSkeleton class implementation
+ *
+ * $LicenseInfo:firstyear=2024&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2024, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llhudeffectresetskeleton.h"
+
+#include "llagent.h"
+#include "llviewerobjectlist.h"
+#include "llvoavatar.h"
+#include "message.h"
+
+// packet layout
+const S32 TARGET_OBJECT = 0; // This is to allow for targetting owned animesh
+const S32 RESET_ANIMATIONS = 16; //This can also be a flags if needed
+const S32 PKT_SIZE = 17;
+
+//-----------------------------------------------------------------------------
+// LLHUDEffectResetSkeleton()
+//-----------------------------------------------------------------------------
+LLHUDEffectResetSkeleton::LLHUDEffectResetSkeleton(const U8 type) : 
+	LLHUDEffect(type)
+{
+}
+
+//-----------------------------------------------------------------------------
+// ~LLHUDEffectResetSkeleton()
+//-----------------------------------------------------------------------------
+LLHUDEffectResetSkeleton::~LLHUDEffectResetSkeleton()
+{
+}
+
+//-----------------------------------------------------------------------------
+// packData()
+//-----------------------------------------------------------------------------
+void LLHUDEffectResetSkeleton::packData(LLMessageSystem *mesgsys)
+{
+	// Pack the default data
+	LLHUDEffect::packData(mesgsys);
+
+	// Pack the type-specific data.  Uses a fun packed binary format.  Whee!
+	U8 packed_data[PKT_SIZE];
+	memset(packed_data, 0, PKT_SIZE);
+
+	// pack both target object and position
+	// position interpreted as offset if target object is non-null
+	if (mTargetObject)
+	{
+		htolememcpy(&(packed_data[TARGET_OBJECT]), mTargetObject->mID.mData, MVT_LLUUID, 16);
+	}
+	else
+	{
+		htolememcpy(&(packed_data[TARGET_OBJECT]), LLUUID::null.mData, MVT_LLUUID, 16);
+	}
+	
+	U8 resetAnimations = (U8)mResetAnimations;
+	htolememcpy(&(packed_data[RESET_ANIMATIONS]), &resetAnimations, MVT_U8, 1);
+
+	mesgsys->addBinaryDataFast(_PREHASH_TypeData, packed_data, PKT_SIZE);
+}
+
+//-----------------------------------------------------------------------------
+// unpackData()
+//-----------------------------------------------------------------------------
+void LLHUDEffectResetSkeleton::unpackData(LLMessageSystem *mesgsys, S32 blocknum)
+{
+	LLVector3d new_target;
+	U8 packed_data[PKT_SIZE];
+
+	
+	LLHUDEffect::unpackData(mesgsys, blocknum);
+	
+	LLUUID source_id;
+	mesgsys->getUUIDFast(_PREHASH_Effect, _PREHASH_AgentID, source_id, blocknum);
+	
+	LLViewerObject *objp = gObjectList.findObject(source_id);
+	if (objp && objp->isAvatar())
+	{
+		setSourceObject(objp);
+	}
+	else
+	{
+		//LL_WARNS() << "Could not find source avatar for ResetSkeleton effect" << LL_ENDL;
+		return;
+	}
+	
+	S32 size = mesgsys->getSizeFast(_PREHASH_Effect, blocknum, _PREHASH_TypeData);
+	if (size != PKT_SIZE)
+	{
+		LL_WARNS() << "ResetSkeleton effect with bad size " << size << LL_ENDL;
+		return;
+	}
+	
+	mesgsys->getBinaryDataFast(_PREHASH_Effect, _PREHASH_TypeData, packed_data, PKT_SIZE, blocknum);
+	
+	LLUUID target_id;
+	htolememcpy(target_id.mData, &(packed_data[TARGET_OBJECT]), MVT_LLUUID, 16);
+
+	if (target_id.isNull())
+	{
+		target_id = source_id;
+	}
+	
+	objp = gObjectList.findObject(target_id);
+
+	if (objp)
+	{
+		setTargetObject(objp);
+	}
+	
+	U8 resetAnimations = 0;
+	htolememcpy(&resetAnimations, &(packed_data[RESET_ANIMATIONS]), MVT_U8, 1);
+	
+	// Pre-emptively assume this is going to be flags in the future.
+	// It isn't needed now, but this will assure that only bit 1 is set
+	mResetAnimations = resetAnimations & 1;
+	
+	update();
+}
+
+//-----------------------------------------------------------------------------
+// setTargetObjectAndOffset()
+//-----------------------------------------------------------------------------
+void LLHUDEffectResetSkeleton::setTargetObject(LLViewerObject *objp)
+{
+	mTargetObject = objp;
+}
+
+
+//-----------------------------------------------------------------------------
+// markDead()
+//-----------------------------------------------------------------------------
+void LLHUDEffectResetSkeleton::markDead()
+{
+	LLHUDEffect::markDead();
+}
+
+void LLHUDEffectResetSkeleton::setSourceObject(LLViewerObject* objectp)
+{
+	// restrict source objects to avatars
+	if (objectp && objectp->isAvatar())
+	{
+		LLHUDEffect::setSourceObject(objectp);
+	}
+}
+
+//-----------------------------------------------------------------------------
+// update()
+//-----------------------------------------------------------------------------
+void LLHUDEffectResetSkeleton::update()
+{
+	// If the target object is dead, set the target object to NULL
+	if (mTargetObject.isNull() || mTargetObject->isDead())
+	{
+		markDead();
+		return;
+	}
+
+	if (mSourceObject.isNull() || mSourceObject->isDead())
+	{
+		markDead();
+		return;
+	}
+	
+	bool owned = false;
+	if(mTargetObject->isAnimatedObject())
+	{
+		owned = mTargetObject->mOwnerID == mSourceObject->getID();
+	}
+	else
+	{
+		owned = mTargetObject->getID() == mSourceObject->getID();
+	}
+
+	if (owned)
+	{
+		if (mTargetObject->isAvatar() || mTargetObject->isAnimatedObject())
+		{
+			((LLVOAvatar*)(LLViewerObject*)mTargetObject)->resetSkeleton(mResetAnimations);
+		}
+	}
+	
+	markDead();
+}
diff --git a/indra/newview/llhudeffectresetskeleton.h b/indra/newview/llhudeffectresetskeleton.h
new file mode 100644
index 0000000000000000000000000000000000000000..ddac84165f39ec24521b1f8dc17e09920c24cd9e
--- /dev/null
+++ b/indra/newview/llhudeffectresetskeleton.h
@@ -0,0 +1,59 @@
+/** 
+ * @file llhudeffectresetskeleton.h
+ * @brief LLHUDEffectResetSkeleton class definition
+ *
+ * $LicenseInfo:firstyear=2024&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2024, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLHUDEFFECTRESETSKELETON_H
+#define LL_LLHUDEFFECTRESETSKELETON_H
+
+#include "llhudeffect.h"
+
+class LLViewerObject;
+class LLVOAvatar;
+
+
+class LLHUDEffectResetSkeleton final : public LLHUDEffect
+{
+public:
+	friend class LLHUDObject;
+
+	/*virtual*/ void markDead();
+	/*virtual*/ void setSourceObject(LLViewerObject* objectp);
+
+	void setTargetObject(LLViewerObject *objp);
+	void setResetAnimations(bool enable){ mResetAnimations = enable; };
+
+protected:
+	LLHUDEffectResetSkeleton(const U8 type);
+	~LLHUDEffectResetSkeleton();
+
+	/*virtual*/ void packData(LLMessageSystem *mesgsys);
+	/*virtual*/ void unpackData(LLMessageSystem *mesgsys, S32 blocknum);
+
+	void update();
+private:
+	bool						mResetAnimations;
+};
+
+#endif // LL_LLHUDEFFECTRESETSKELETON_H
diff --git a/indra/newview/llhudobject.cpp b/indra/newview/llhudobject.cpp
index 36a28b826961422730e49f225b6c23a37c901429..590ea3b34ec1e5cd473ce7585935fceec2ce8a5a 100644
--- a/indra/newview/llhudobject.cpp
+++ b/indra/newview/llhudobject.cpp
@@ -36,6 +36,7 @@
 #include "llhudeffecttrail.h"
 #include "llhudeffectlookat.h"
 #include "llhudeffectpointat.h"
+#include "llhudeffectresetskeleton.h"
 #include "llhudnametag.h"
 #include "llvoicevisualizer.h"
 
@@ -241,6 +242,9 @@ LLHUDEffect *LLHUDObject::addHUDEffect(const U8 type)
     case LL_HUD_EFFECT_BLOB:
         hud_objectp = new LLHUDEffectBlob(type);
         break;
+    case LL_HUD_EFFECT_RESET_SKELETON:
+        hud_objectp = new LLHUDEffectResetSkeleton(type);
+        break;
     default:
         LL_WARNS() << "Unknown type of hud effect:" << (U32) type << LL_ENDL;
     }
diff --git a/indra/newview/llhudobject.h b/indra/newview/llhudobject.h
index 68149daceae9043f63c2061528fb4a634091f35c..9c4eaf3d69d56e69b825377962dc1dacc0500bc7 100644
--- a/indra/newview/llhudobject.h
+++ b/indra/newview/llhudobject.h
@@ -100,7 +100,8 @@ class LLHUDObject : public LLRefCount
         LL_HUD_EFFECT_POINTAT,
         LL_HUD_EFFECT_VOICE_VISUALIZER, // Ventrella
         LL_HUD_NAME_TAG,
-        LL_HUD_EFFECT_BLOB
+        LL_HUD_EFFECT_BLOB,
+        LL_HUD_EFFECT_RESET_SKELETON
     };
 protected:
     static void sortObjects();
diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp
index 20501f17e54b9233e7d4bb33fd4721b569a44f38..6d80214ed780ac1dda909c342b3f3ff2e0e2f07e 100644
--- a/indra/newview/llinventoryfunctions.cpp
+++ b/indra/newview/llinventoryfunctions.cpp
@@ -3253,7 +3253,7 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root
 
                 for (LLInventoryModel::item_array_t::value_type& item : items)
                 {
-                    if (get_is_item_worn(item))
+                    if (!item->getIsLinkType() && get_is_item_worn(item))
                     {
                         has_worn = true;
                         LLWearableType::EType type = item->getWearableType();
@@ -3273,7 +3273,7 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root
                 }
             }
             LLViewerInventoryItem* item = gInventory.getItem(obj_id);
-            if (item && get_is_item_worn(item))
+            if (item && !item->getIsLinkType() && get_is_item_worn(item))
             {
                 has_worn = true;
                 LLWearableType::EType type = item->getWearableType();
diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp
index 4dae97088f194dee43ca41865799aad6e4c6f85b..cc7afdfd332cbf3fa4e91c9567434bca85c85ca7 100644
--- a/indra/newview/llinventorygallery.cpp
+++ b/indra/newview/llinventorygallery.cpp
@@ -1988,7 +1988,7 @@ void LLInventoryGallery::deleteSelection()
 
             for (LLInventoryModel::item_array_t::value_type& item : items)
             {
-                if (get_is_item_worn(item))
+                if (!item->getIsLinkType() && get_is_item_worn(item))
                 {
                     has_worn = true;
                     LLWearableType::EType type = item->getWearableType();
@@ -2009,7 +2009,7 @@ void LLInventoryGallery::deleteSelection()
         }
 
         LLViewerInventoryItem* item = gInventory.getItem(id);
-        if (item && get_is_item_worn(item))
+        if (item && !item->getIsLinkType() && get_is_item_worn(item))
         {
             has_worn = true;
             LLWearableType::EType type = item->getWearableType();            
diff --git a/indra/newview/llinventorygallerymenu.cpp b/indra/newview/llinventorygallerymenu.cpp
index 3bd0fc098ce9f8b13c125436ddfce3be7ceb801e..79dbde6225d2e0d7e1f15ff79f3b649b24b79f9c 100644
--- a/indra/newview/llinventorygallerymenu.cpp
+++ b/indra/newview/llinventorygallerymenu.cpp
@@ -239,11 +239,11 @@ void LLInventoryGalleryContextMenu::doToSelected(const LLSD& userdata)
     }
     else if ("copy_slurl" == action)
     {
-        boost::function<void(LLLandmark*)> copy_slurl_cb = [](LLLandmark* landmark)
+        std::function<void(LLLandmark*)> copy_slurl_cb = [](LLLandmark* landmark)
         {
             LLVector3d global_pos;
             landmark->getGlobalPos(global_pos);
-            boost::function<void(std::string& slurl)> copy_slurl_to_clipboard_cb = [](std::string& slurl)
+            std::function<void(std::string& slurl)> copy_slurl_to_clipboard_cb = [](std::string& slurl)
             {
                gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(slurl));
                LLSD args;
diff --git a/indra/newview/lllandmarkactions.h b/indra/newview/lllandmarkactions.h
index 29ffc1dcee4646823a5c20a82a72cca4aa75825c..b19132e1a3521a2a4bddb17385c286094f4a249c 100644
--- a/indra/newview/lllandmarkactions.h
+++ b/indra/newview/lllandmarkactions.h
@@ -39,8 +39,8 @@ class LLLandmark;
 class LLLandmarkActions
 {
 public:
-    typedef boost::function<void(std::string& slurl)> slurl_callback_t;
-    typedef boost::function<void(std::string& slurl, S32 x, S32 y, S32 z)> region_name_and_coords_callback_t;
+    typedef std::function<void(std::string& slurl)> slurl_callback_t;
+    typedef std::function<void(std::string& slurl, S32 x, S32 y, S32 z)> region_name_and_coords_callback_t;
 
     /**
      * @brief Fetches landmark LLViewerInventoryItems for the given landmark name.
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index a2b431b890bede1f1f2169771d1e4b2987e3418e..e445cb31925d0ddf8e8db8c247b66d8eedf25204 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -590,6 +590,19 @@ bool idle_startup()
                 {
                     LLMessageConfig::initClass("viewer", gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, ""));
                 }
+            #elif LL_LINUX
+                // On the windows dev builds, unpackaged, the message.xml file will
+                // be located in indra/build-vc**/newview/<config>/app_settings.
+                std::string message_path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "message.xml");
+
+                if (!LLFile::isfile(message_path))
+                {
+                    LLMessageConfig::initClass("viewer", gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, ".." ,"app_settings", ""));
+                }
+                else
+                {
+                    LLMessageConfig::initClass("viewer", gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, ""));
+                }
             #else
                 LLMessageConfig::initClass("viewer", gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, ""));
             #endif
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 4da296410d31dc0aa5ef923b52d1e7cbb00e5076..858bc5b4246c6afa6018a720955d3c5b39f4b1ae 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -92,6 +92,7 @@
 #include "lltoolface.h"
 #include "llhints.h"
 #include "llhudeffecttrail.h"
+#include "llhudeffectresetskeleton.h"
 #include "llhudmanager.h"
 #include "llimview.h"
 #include "llinventorybridge.h"
@@ -6797,10 +6798,19 @@ class LLAvatarResetSkeleton: public view_listener_t
         LLVOAvatar* avatar = find_avatar_from_object(LLSelectMgr::getInstance()->getSelection()->getPrimaryObject());
         if(avatar)
         {
-            avatar->resetSkeleton(false);
-// [SL:KB] - Patch: Appearance-RefreshAttachments | Checked: Catznip-5.3
-            avatar->rebuildAttachments();
-// [/SL:KB]
+            bool owned = false;
+            if(avatar->isAnimatedObject())
+            {
+                owned = avatar->mOwnerID == gAgent.getID();
+            }
+            else
+            {
+                owned = avatar->getID() == gAgent.getID();
+            }
+            LLHUDEffectResetSkeleton* effectp = (LLHUDEffectResetSkeleton*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_RESET_SKELETON, owned);
+            effectp->setSourceObject(gAgentAvatarp);
+            effectp->setTargetObject((LLViewerObject*)avatar);
+            effectp->setResetAnimations(false);
         }
         return true;
     }
@@ -6827,10 +6837,19 @@ class LLAvatarResetSkeletonAndAnimations : public view_listener_t
         LLVOAvatar* avatar = find_avatar_from_object(LLSelectMgr::getInstance()->getSelection()->getPrimaryObject());
         if (avatar)
         {
-            avatar->resetSkeleton(true);
-// [SL:KB] - Patch: Appearance-RefreshAttachments | Checked: Catznip-5.3
-            avatar->rebuildAttachments();
-// [/SL:KB]
+            bool owned = false;
+            if(avatar->isAnimatedObject())
+            {
+                owned = avatar->mOwnerID == gAgent.getID();
+            }
+            else
+            {
+                owned = avatar->getID() == gAgent.getID();
+            }
+            LLHUDEffectResetSkeleton* effectp = (LLHUDEffectResetSkeleton*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_RESET_SKELETON, owned);
+            effectp->setSourceObject(gAgentAvatarp);
+            effectp->setTargetObject((LLViewerObject*)avatar);
+            effectp->setResetAnimations(true);
         }
         return true;
     }
@@ -6843,11 +6862,26 @@ class LLAvatarResetSelfSkeletonAndAnimations : public view_listener_t
         LLVOAvatar* avatar = find_avatar_from_object(LLSelectMgr::getInstance()->getSelection()->getPrimaryObject());
         if (avatar)
         {
-            avatar->resetSkeleton(true);
+            bool owned = false;
+            if(avatar->isAnimatedObject())
+            {
+                owned = avatar->mOwnerID == gAgent.getID();
+            }
+            else
+            {
+                owned = avatar->getID() == gAgent.getID();
+            }
+            LLHUDEffectResetSkeleton* effectp = (LLHUDEffectResetSkeleton*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_RESET_SKELETON, owned);
+            effectp->setSourceObject(gAgentAvatarp);
+            effectp->setTargetObject((LLViewerObject*)avatar);
+            effectp->setResetAnimations(true);
         }
         else
         {
-            gAgentAvatarp->resetSkeleton(true);
+            LLHUDEffectResetSkeleton* effectp = (LLHUDEffectResetSkeleton*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_RESET_SKELETON, true);
+            effectp->setSourceObject(gAgentAvatarp);
+            effectp->setTargetObject(gAgentAvatarp);
+            effectp->setResetAnimations(true);
         }
         return true;
     }
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index 326ebe7ed447a725fcbb17febb9813741794dc0b..c17dbcf4ead2b3ac999b43cb67202f646986ebbe 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -3090,24 +3090,33 @@ void LLViewerObject::fetchInventoryFromServer()
         delete mInventory;
         mInventory = NULL;
 
-        // Results in processTaskInv
-        LLMessageSystem* msg = gMessageSystem;
-        msg->newMessageFast(_PREHASH_RequestTaskInventory);
-        msg->nextBlockFast(_PREHASH_AgentData);
-        msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
-        msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
-        msg->nextBlockFast(_PREHASH_InventoryData);
-        msg->addU32Fast(_PREHASH_LocalID, mLocalID);
-        msg->sendReliable(mRegionp->getHost());
-
         // This will get reset by doInventoryCallback or processTaskInv
         mInvRequestState = INVENTORY_REQUEST_PENDING;
+
+        if (mRegionp && !mRegionp->getCapability("RequestTaskInventory").empty())
+        {
+            LLCoros::instance().launch("LLViewerObject::fetchInventoryFromCapCoro()",
+                                       boost::bind(&LLViewerObject::fetchInventoryFromCapCoro, mID));
+        }
+        else
+        {
+            LL_WARNS() << "Using old task inventory path!" << LL_ENDL;
+            // Results in processTaskInv
+            LLMessageSystem *msg = gMessageSystem;
+            msg->newMessageFast(_PREHASH_RequestTaskInventory);
+            msg->nextBlockFast(_PREHASH_AgentData);
+            msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+            msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+            msg->nextBlockFast(_PREHASH_InventoryData);
+            msg->addU32Fast(_PREHASH_LocalID, mLocalID);
+            msg->sendReliable(mRegionp->getHost());
+        }
     }
 }
 
 void LLViewerObject::fetchInventoryDelayed(const F64 &time_seconds)
 {
-    // unless already waiting, drop previous request and shedule an update
+    // unless already waiting, drop previous request and schedule an update
     if (mInvRequestState != INVENTORY_REQUEST_WAIT)
     {
         if (mInvRequestXFerId != 0)
@@ -3138,6 +3147,80 @@ void LLViewerObject::fetchInventoryDelayedCoro(const LLUUID task_inv, const F64
     }
 }
 
+//static
+void LLViewerObject::fetchInventoryFromCapCoro(const LLUUID task_inv)
+{
+    LLViewerObject *obj = gObjectList.findObject(task_inv);
+    if (obj)
+    {
+        LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+        LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
+                                   httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("TaskInventoryRequest", httpPolicy));
+        LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
+        std::string url = obj->mRegionp->getCapability("RequestTaskInventory") + "?task_id=" + obj->mID.asString();
+        // If we already have a copy of the inventory then add it so the server won't re-send something we already have.
+        // We expect this case to crop up in the case of failed inventory mutations, but it might happen otherwise as well.
+        if (obj->mInventorySerialNum && obj->mInventory)
+            url += "&inventory_serial=" + std::to_string(obj->mInventorySerialNum);
+
+        obj->mInvRequestState = INVENTORY_XFER;
+        LLSD result = httpAdapter->getAndSuspend(httpRequest, url);
+
+        LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+        LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+        // Object may have gone away while we were suspended, double-check that it still exists
+        obj = gObjectList.findObject(task_inv);
+        if (!obj)
+        {
+            LL_WARNS() << "Object " << task_inv << " went away while fetching inventory, dropping result" << LL_ENDL;
+            return;
+        }
+
+        bool potentially_stale = false;
+        if (status)
+        {
+            // Dealing with inventory serials is kind of funky. They're monotonically increasing and 16 bits,
+            // so we expect them to overflow, but we can use inv serial < expected serial as a signal that we may
+            // have mutated the task inventory since we kicked off the request, and those mutations may have not
+            // been taken into account yet. Of course, those mutations may have actually failed which would result
+            // in the inv serial never increasing.
+            //
+            // When we detect this case, set the expected inv serial to the inventory serial we actually received
+            // and kick off a re-request after a slight delay.
+            S16 serial = (S16)result["inventory_serial"].asInteger();
+            potentially_stale = serial < obj->mExpectedInventorySerialNum;
+            LL_INFOS() << "Inventory loaded for " << task_inv << LL_ENDL;
+            obj->mInventorySerialNum = serial;
+            obj->mExpectedInventorySerialNum = serial;
+            obj->loadTaskInvLLSD(result);
+        }
+        else if (status.getType() == 304)
+        {
+            LL_INFOS() << "Inventory wasn't changed on server!" << LL_ENDL;
+            obj->mInvRequestState = INVENTORY_REQUEST_STOPPED;
+            // Even though it wasn't necessary to send a response, we still may have mutated
+            // the inventory since we kicked off the request, check for that case.
+            potentially_stale = obj->mInventorySerialNum < obj->mExpectedInventorySerialNum;
+            // Set this to what we already have so that we don't re-request a second time.
+            obj->mExpectedInventorySerialNum = obj->mInventorySerialNum;
+        }
+        else
+        {
+            // Not sure that there's anything sensible we can do to recover here, retrying in a loop would be bad.
+            LL_WARNS() << "Error status while requesting task inventory: " << status.toString() << LL_ENDL;
+            obj->mInvRequestState = INVENTORY_REQUEST_STOPPED;
+        }
+
+        if (potentially_stale)
+        {
+            // Stale? I guess we can use what we got for now, but we'll have to re-request
+            LL_WARNS() << "Stale inv_serial? Re-requesting." << LL_ENDL;
+            obj->fetchInventoryDelayed(INVENTORY_UPDATE_WAIT_TIME_OUTDATED);
+        }
+    }
+}
+
 LLControlAvatar *LLViewerObject::getControlAvatar()
 {
     return getRootEdit()->mControlAvatar.get();
@@ -3322,6 +3405,20 @@ void LLViewerObject::processTaskInv(LLMessageSystem* msg, void** user_data)
     S16 serial = 0;
     msg->getS16Fast(_PREHASH_InventoryData, _PREHASH_Serial, serial);
 
+    if (object->mRegionp && !object->mRegionp->getCapability("RequestTaskInventory").empty())
+    {
+        // It seems that simulator may ask us to re-download the task inventory if an update to the inventory
+        // happened out-of-band while we had the object selected (like if a script is saved.)
+        //
+        // If we're meant to use the HTTP capability, ignore the contents of the UDP message and fetch the
+        // inventory via the CAP so that we don't flow down the UDP inventory request path unconditionally here.
+        // We shouldn't need to wait, as any updates should already be ready to fetch by this point.
+        LL_INFOS() << "Handling unsolicited ReplyTaskInventory for " << task_id << LL_ENDL;
+        object->mExpectedInventorySerialNum = serial;
+        object->fetchInventoryFromServer();
+        return;
+    }
+
     if (serial == object->mInventorySerialNum
         && serial < object->mExpectedInventorySerialNum)
     {
@@ -3529,6 +3626,47 @@ BOOL LLViewerObject::loadTaskInvFile(const std::string& filename)
     return TRUE;
 }
 
+void LLViewerObject::loadTaskInvLLSD(const LLSD& inv_result)
+{
+    if (inv_result.has("contents"))
+    {
+        if(mInventory)
+        {
+            mInventory->clear(); // will deref and delete it
+        }
+        else
+        {
+            mInventory = new LLInventoryObject::object_list_t;
+        }
+
+        // Synthesize the "Contents" category, the viewer expects it, but it isn't sent.
+        LLPointer<LLInventoryObject> inv = new LLInventoryObject(mID, LLUUID::null, LLAssetType::AT_CATEGORY, "Contents");
+        mInventory->push_front(inv);
+
+        const LLSD& inventory = inv_result["contents"];
+        for (const auto& inv_entry : llsd::inArray(inventory))
+        {
+            if (inv_entry.has("item_id"))
+            {
+                LLPointer<LLViewerInventoryItem> inv = new LLViewerInventoryItem;
+                inv->unpackMessage(inv_entry);
+                mInventory->push_front(inv);
+            }
+            else
+            {
+                LL_WARNS_ONCE() << "Unknown inventory entry while reading from inventory file. Entry: '"
+                                << inv_entry << "'" << LL_ENDL;
+            }
+        }
+    }
+    else
+    {
+        LL_WARNS() << "unable to load task inventory: " << inv_result << LL_ENDL;
+        return;
+    }
+    doInventoryCallback();
+}
+
 void LLViewerObject::doInventoryCallback()
 {
     for (callback_list_t::iterator iter = mInventoryCallbacks.begin();
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
index c5176503eb964855db62f477709fa628616f8b2b..4e002560a28677ea2842fe63b368d00068b48a75 100644
--- a/indra/newview/llviewerobject.h
+++ b/indra/newview/llviewerobject.h
@@ -730,6 +730,7 @@ class LLViewerObject
     // forms task inventory request after some time passed, marks request as pending
     void fetchInventoryDelayed(const F64 &time_seconds);
     static void fetchInventoryDelayedCoro(const LLUUID task_inv, const F64 time_seconds);
+    static void fetchInventoryFromCapCoro(const LLUUID task_inv);
 
 public:
     //
@@ -861,6 +862,7 @@ class LLViewerObject
 
     static void processTaskInvFile(void** user_data, S32 error_code, LLExtStat ext_status);
     BOOL loadTaskInvFile(const std::string& filename);
+    void loadTaskInvLLSD(const LLSD &inv_result);
     void doInventoryCallback();
 
     BOOL isOnMap();
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 54f0ea3d5d8c4cdc7a90de3ea52d67ff40659348..4a8acf74cb7fd3dd08d513e2697b570d2eaaec7e 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -3400,6 +3400,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
         capabilityNames.append("FetchInventory2");
         capabilityNames.append("FetchInventoryDescendents2");
         capabilityNames.append("IncrementCOFVersion");
+        capabilityNames.append("RequestTaskInventory");
         AISAPI::getCapNames(capabilityNames);
     }
 
diff --git a/indra/newview/llworldmapmessage.h b/indra/newview/llworldmapmessage.h
index e81aaae4e2c3f95d89054fd51955f9c937fdb602..d20259672579336d4e836fe5900bbe99df733876 100644
--- a/indra/newview/llworldmapmessage.h
+++ b/indra/newview/llworldmapmessage.h
@@ -38,7 +38,7 @@ class LLWorldMapMessage final : public LLSingleton<LLWorldMapMessage>
     ~LLWorldMapMessage();
 
 public:
-    typedef boost::function<void(U64 region_handle, const std::string& url, const LLUUID& snapshot_id, bool teleport)>
+    typedef std::function<void(U64 region_handle, const std::string& url, const LLUUID& snapshot_id, bool teleport)>
         url_callback_t;
 
     // Process incoming answers to map stuff requests