Repository: microsoft/terminal (language: cpp)

Issue:
Session restoration happens even after I explicitly close all tabs
### Windows Terminal version

Preview 1.20

### Windows build number

_No response_

### Other Software

_No response_

### Steps to reproduce

1. Open 5 tabs
2. Close them one by one
3. <kbd>Win+R</kbd>, `wt`

### Expected Behavior

_No response_

### Actual Behavior

It opens 5 tabs.


Discussion / hints:

Fix patch:
diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt
index 28b9b790d7b..1ce77a78ad5 100644
--- a/.github/actions/spelling/expect/expect.txt
+++ b/.github/actions/spelling/expect/expect.txt
@@ -597,6 +597,7 @@ FECF
 FEEF
 fesb
 FFAF
+ffd
 FFDE
 FFFDb
 fgbg
@@ -757,6 +758,7 @@ hdr
 HDROP
 hdrstop
 HEIGHTSCROLL
+hfind
 hfont
 hfontresource
 hglobal
diff --git a/src/buffer/out/TextColor.cpp b/src/buffer/out/TextColor.cpp
index fa84adfac8d..0840ec19f5f 100644
--- a/src/buffer/out/TextColor.cpp
+++ b/src/buffer/out/TextColor.cpp
@@ -62,6 +62,11 @@ bool TextColor::CanBeBrightened() const noexcept
     return IsIndex16() || IsDefault();
 }
 
+ColorType TextColor::GetType() const noexcept
+{
+    return _meta;
+}
+
 bool TextColor::IsLegacy() const noexcept
 {
     return (IsIndex16() || IsIndex256()) && _index < 16;
@@ -280,15 +285,3 @@ BYTE TextColor::GetLegacyIndex(const BYTE defaultIndex) const noexcept
         return til::at(CompressedRgbToIndex16, compressedRgb);
     }
 }
-
-// Method Description:
-// - Return a COLORREF containing our stored value. Will return garbage if this
-//attribute is not a RGB attribute.
-// Arguments:
-// - <none>
-// Return Value:
-// - a COLORREF containing our stored value
-COLORREF TextColor::GetRGB() const noexcept
-{
-    return RGB(_red, _green, _blue);
-}
diff --git a/src/buffer/out/TextColor.h b/src/buffer/out/TextColor.h
index 0b71078fbf1..3070f816974 100644
--- a/src/buffer/out/TextColor.h
+++ b/src/buffer/out/TextColor.h
@@ -119,6 +119,7 @@ struct TextColor
     }
 
     bool CanBeBrightened() const noexcept;
+    ColorType GetType() const noexcept;
     bool IsLegacy() const noexcept;
     bool IsIndex16() const noexcept;
     bool IsIndex256() const noexcept;
@@ -133,12 +134,11 @@ struct TextColor
     COLORREF GetColor(const std::array<COLORREF, TABLE_SIZE>& colorTable, const size_t defaultIndex, bool brighten = false) const noexcept;
     BYTE GetLegacyIndex(const BYTE defaultIndex) const noexcept;
 
-    constexpr BYTE GetIndex() const noexcept
-    {
-        return _index;
-    }
-
-    COLORREF GetRGB() const noexcept;
+    constexpr BYTE GetIndex() const noexcept { return _index; }
+    constexpr BYTE GetR() const noexcept { return _red; }
+    constexpr BYTE GetG() const noexcept { return _green; }
+    constexpr BYTE GetB() const noexcept { return _blue; }
+    constexpr COLORREF GetRGB() const noexcept { return RGB(_red, _green, _blue); }
 
     static constexpr BYTE TransposeLegacyIndex(const size_t index)
     {
diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp
index 5530dedb739..62371474adc 100644
--- a/src/buffer/out/textBuffer.cpp
+++ b/src/buffer/out/textBuffer.cpp
@@ -11,7 +11,6 @@
 #include "UTextAdapter.h"
 #include "../../types/inc/GlyphWidth.hpp"
 #include "../renderer/base/renderer.hpp"
-#include "../types/inc/convert.hpp"
 #include "../types/inc/utils.hpp"
 
 using namespace Microsoft::Console;
@@ -2508,6 +2507,237 @@ void TextBuffer::_AppendRTFText(std::string& contentBuilder, const std::wstring_
     }
 }
 
+void TextBuffer::Serialize(const wchar_t* destination) const
+{
+    const wil::unique_handle file{ CreateFileW(destination, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr) };
+    THROW_LAST_ERROR_IF(!file);
+
+    static constexpr size_t writeThreshold = 32 * 1024;
+    std::wstring buffer;
+    buffer.reserve(writeThreshold + writeThreshold / 2);
+    buffer.push_back(L'\uFEFF');
+
+    const til::CoordType lastRowWithText = GetLastNonSpaceCharacter(nullptr).y;
+    CharacterAttributes previousAttr = CharacterAttributes::Unused1;
+    TextColor previousFg;
+    TextColor previousBg;
+    TextColor previousUl;
+    uint16_t previousHyperlinkId = 0;
+
+    // This iterates through each row. The exit condition is at the end
+    // of the for() loop so that we can properly handle file flushing.
+    for (til::CoordType currentRow = 0;; currentRow++)
+    {
+        const auto& row = GetRowByOffset(currentRow);
+
+        if (const auto lr = row.GetLineRendition(); lr != LineRendition::SingleWidth)
+        {
+            static constexpr std::wstring_view mappings[] = {
+                L"\x1b#6", // LineRendition::DoubleWidth
+                L"\x1b#3", // LineRendition::DoubleHeightTop
+                L"\x1b#4", // LineRendition::DoubleHeightBottom
+            };
+            const auto idx = std::clamp(static_cast<int>(lr) - 1, 0, 2);
+            buffer.append(til::at(mappings, idx));
+        }
+
+        const auto& runs = row.Attributes().runs();
+        auto it = runs.begin();
+        const auto end = runs.end();
+        const auto last = end - 1;
+        til::CoordType oldX = 0;
+
+        for (; it != end; ++it)
+        {
+            const auto attr = it->value.GetCharacterAttributes();
+            const auto hyperlinkId = it->value.GetHyperlinkId();
+            const auto fg = it->value.GetForeground();
+            const auto bg = it->value.GetBackground();
+            const auto ul = it->value.GetUnderlineColor();
+
+            if (previousAttr != attr)
+            {
+                const auto attrDelta = attr ^ previousAttr;
+
+                {
+                    struct Mapping
+                    {
+                        CharacterAttributes attr;
+                        uint8_t change[2]; // [0] = off, [1] = on
+                    };
+                    static constexpr Mapping mappings[] = {
+                        { CharacterAttributes::Intense, { 22, 1 } },
+                        { CharacterAttributes::Italics, { 23, 3 } },
+                        { CharacterAttributes::Blinking, { 25, 5 } },
+                        { CharacterAttributes::Invisible, { 28, 8 } },
+                        { CharacterAttributes::CrossedOut, { 29, 9 } },
+                        { CharacterAttributes::Faint, { 22, 2 } },
+                        { CharacterAttributes::TopGridline, { 55, 53 } },
+                        { CharacterAttributes::ReverseVideo, { 27, 7 } },
+                        { CharacterAttributes::BottomGridline, { 24, 4 } },
+                    };
+                    for (const auto& mapping : mappings)
+                    {
+                        if (WI_IsAnyFlagSet(attrDelta, mapping.attr))
+                        {
+                            const auto n = til::at(mapping.change, WI_IsAnyFlagSet(attr, mapping.attr));
+                            fmt::format_to(std::back_inserter(buffer), FMT_COMPILE(L"\x1b[{}m"), n);
+                        }
+                    }
+                }
+
+                if (WI_IsAnyFlagSet(attrDelta, CharacterAttributes::UnderlineStyle))
+                {
+                    static constexpr std::wstring_view mappings[] = {
+                        L"\x1b[24m", // UnderlineStyle::NoUnderline
+                        L"\x1b[4m", // UnderlineStyle::SinglyUnderlined
+                        L"\x1b[21m", // UnderlineStyle::DoublyUnderlined
+                        L"\x1b[4:3m", // UnderlineStyle::CurlyUnderlined
+                        L"\x1b[4:4m", // UnderlineStyle::DottedUnderlined
+                        L"\x1b[4:5m", // UnderlineStyle::DashedUnderlined
+                    };
+
+                    auto idx = WI_EnumValue(it->value.GetUnderlineStyle());
+                    if (idx >= std::size(mappings))
+                    {
+                        idx = 1; // UnderlineStyle::SinglyUnderlined
+                    }
+
+                    buffer.append(til::at(mappings, idx));
+                }
+
+                previousAttr = attr;
+            }
+
+            if (previousFg != fg)
+            {
+                switch (fg.GetType())
+                {
+                case ColorType::IsDefault:
+                    buffer.append(L"\x1b[39m");
+                    break;
+                case ColorType::IsIndex16:
+                {
+                    uint8_t index = WI_IsFlagSet(fg.GetIndex(), 8) ? 90 : 30;
+                    index += fg.GetIndex() & 7;
+                    fmt::format_to(std::back_inserter(buffer), FMT_COMPILE(L"\x1b[{}m"), index);
+                    break;
+                }
+                case ColorType::IsIndex256:
+                    fmt::format_to(std::back_inserter(buffer), FMT_COMPILE(L"\x1b[38;5;{}m"), fg.GetIndex());
+                    break;
+                case ColorType::IsRgb:
+                    fmt::format_to(std::back_inserter(buffer), FMT_COMPILE(L"\x1b[38;2;{};{};{}m"), fg.GetR(), fg.GetG(), fg.GetB());
+                    break;
+                default:
+                    break;
+                }
+                previousFg = fg;
+            }
+
+            if (previousBg != bg)
+            {
+                switch (bg.GetType())
+                {
+                case ColorType::IsDefault:
+                    buffer.append(L"\x1b[49m");
+                    break;
+                case ColorType::IsIndex16:
+                {
+                    uint8_t index = WI_IsFlagSet(bg.GetIndex(), 8) ? 100 : 40;
+                    index += bg.GetIndex() & 7;
+                    fmt::format_to(std::back_inserter(buffer), FMT_COMPILE(L"\x1b[{}m"), index);
+                    break;
+                }
+                case ColorType::IsIndex256:
+                    fmt::format_to(std::back_inserter(buffer), FMT_COMPILE(L"\x1b[48;5;{}m"), bg.GetIndex());
+                    break;
+                case ColorType::IsRgb:
+                    fmt::format_to(std::back_inserter(buffer), FMT_COMPILE(L"\x1b[48;2;{};{};{}m"), bg.GetR(), bg.GetG(), bg.GetB());
+                    break;
+                default:
+                    break;
+                }
+                previousBg = bg;
+            }
+
+            if (previousUl != ul)
+            {
+                switch (fg.GetType())
+                {
+                case ColorType::IsDefault:
+                    buffer.append(L"\x1b[59m");
+                    break;
+                case ColorType::IsIndex256:
+                    fmt::format_to(std::back_inserter(buffer), FMT_COMPILE(L"\x1b[58:5:{}m"), ul.GetIndex());
+                    break;
+                case ColorType::IsRgb:
+                    fmt::format_to(std::back_inserter(buffer), FMT_COMPILE(L"\x1b[58:2::{}:{}:{}m"), ul.GetR(), ul.GetG(), ul.GetB());
+                    break;
+                default:
+                    break;
+                }
+                previousUl = ul;
+            }
+
+            if (previousHyperlinkId != hyperlinkId)
+            {
+                if (hyperlinkId)
+                {
+                    const auto uri = GetHyperlinkUriFromId(hyperlinkId);
+                    if (!uri.empty())
+                    {
+                        buffer.append(L"\x1b]8;;");
+                        buffer.append(uri);
+                        buffer.append(L"\x1b\\");
+                        previousHyperlinkId = hyperlinkId;
+                    }
+                }
+                else
+                {
+                    buffer.append(L"\x1b]8;;\x1b\\");
+                    previousHyperlinkId = 0;
+                }
+            }
+
+            auto newX = oldX + it->length;
+            // Trim whitespace with default attributes from the end of each line.
+            if (it == last && it->value == TextAttribute{})
+            {
+                // This can result in oldX > newX, but that's okay because GetText()
+                // is robust against that and returns an empty string.
+                newX = row.MeasureRight();
+            }
+
+            buffer.append(row.GetText(oldX, newX));
+            oldX = newX;
+        }
+
+        const auto moreRowsRemaining = currentRow < lastRowWithText;
+
+        if (!row.WasWrapForced() || !moreRowsRemaining)
+        {
+            buffer.append(L"\r\n");
+        }
+
+        if (buffer.size() >= writeThreshold || !moreRowsRemaining)
+        {
+            const auto fileSize = gsl::narrow<DWORD>(buffer.size() * sizeof(wchar_t));
+            DWORD bytesWritten = 0;
+            THROW_IF_WIN32_BOOL_FALSE(WriteFile(file.get(), buffer.data(), fileSize, &bytesWritten, nullptr));
+            if (bytesWritten != fileSize)
+            {
+                THROW_WIN32_MSG(ERROR_WRITE_FAULT, "failed to write");
+            }
+        }
+
+        if (!moreRowsRemaining)
+        {
+            break;
+        }
+    }
+}
+
 // Function Description:
 // - Reflow the contents from the old buffer into the new buffer. The new buffer
 //   can have different dimensions than the old buffer. If it does, then this
diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp
index 419e01471e5..8093aea9af8 100644
--- a/src/buffer/out/textBuffer.hpp
+++ b/src/buffer/out/textBuffer.hpp
@@ -319,6 +319,8 @@ class TextBuffer final
                        const bool isIntenseBold,
                        std::function<std::tuple<COLORREF, COLORREF, COLORREF>(const TextAttribute&)> GetAttributeColors) const noexcept;
 
+    void Serialize(const wchar_t* destination) const;
+
     struct PositionInformation
     {
         til::CoordType mutableViewportTop{ 0 };
diff --git a/src/cascadia/Remoting/GetWindowLayoutArgs.cpp b/src/cascadia/Remoting/GetWindowLayoutArgs.cpp
deleted file mode 100644
index f2cc01df40c..00000000000
--- a/src/cascadia/Remoting/GetWindowLayoutArgs.cpp
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (c) Microsoft Corporation.
-// Licensed under the MIT license.
-#include "pch.h"
-#include "GetWindowLayoutArgs.h"
-#include "GetWindowLayoutArgs.g.cpp"
diff --git a/src/cascadia/Remoting/GetWindowLayoutArgs.h b/src/cascadia/Remoting/GetWindowLayoutArgs.h
deleted file mode 100644
index 097e375bc70..00000000000
--- a/src/cascadia/Remoting/GetWindowLayoutArgs.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*++
-Copyright (c) Microsoft Corporation
-Licensed under the MIT license.
-
-Class Name:
-- GetWindowLayoutArgs.h
-
-Abstract:
-- This is a helper class for getting the window layout from a peasant.
-  Depending on if we are running on the monarch or on a peasant we might need
-  to switch what thread we are executing on. This gives us the option of
-  either returning the json result synchronously, or as a promise.
---*/
-
-#pragma once
-
-#include "GetWindowLayoutArgs.g.h"
-
-namespace winrt::Microsoft::Terminal::Remoting::implementation
-{
-    struct GetWindowLayoutArgs : public GetWindowLayoutArgsT<GetWindowLayoutArgs>
-    {
-        WINRT_PROPERTY(winrt::hstring, WindowLayoutJson, L"");
-        WINRT_PROPERTY(winrt::Windows::Foundation::IAsyncOperation<winrt::hstring>, WindowLayoutJsonAsync, nullptr)
-    };
-}
-
-namespace winrt::Microsoft::Terminal::Remoting::factory_implementation
-{
-    BASIC_FACTORY(GetWindowLayoutArgs);
-}
diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj
index ce78fc9c4b2..b5c7ada4790 100644
--- a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj
+++ b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj
@@ -37,12 +37,6 @@
     <ClInclude Include="WindowActivatedArgs.h">
       <DependentUpon>Peasant.idl</DependentUpon>
     </ClInclude>
-    <ClInclude Include="GetWindowLayoutArgs.h">
-      <DependentUpon>Peasant.idl</DependentUpon>
-    </ClInclude>
-    <ClInclude Include="QuitAllRequestedArgs.h">
-      <DependentUpon>Monarch.idl</DependentUpon>
-    </ClInclude>
     <ClInclude Include="pch.h" />
     <ClInclude Include="MonarchFactory.h" />
     <ClInclude Include="Peasant.h">
@@ -78,12 +72,6 @@
     <ClCompile Include="WindowActivatedArgs.cpp">
       <DependentUpon>Peasant.idl</DependentUpon>
     </ClCompile>
-    <ClCompile Include="GetWindowLayoutArgs.cpp">
-      <DependentUpon>Peasant.idl</DependentUpon>
-    </ClCompile>
-    <ClCompile Include="QuitAllRequestedArgs.cpp">
-      <DependentUpon>Monarch.idl</DependentUpon>
-    </ClCompile>
     <ClCompile Include="pch.cpp">
       <PrecompiledHeader>Create</PrecompiledHeader>
     </ClCompile>
diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp
index 6b48650c5f2..63e74b0fae1 100644
--- a/src/cascadia/Remoting/Monarch.cpp
+++ b/src/cascadia/Remoting/Monarch.cpp
@@ -6,7 +6,6 @@
 #include "Monarch.h"
 #include "CommandlineArgs.h"
 #include "FindTargetWindowArgs.h"
-#include "QuitAllRequestedArgs.h"
 #include "ProposeCommandlineResult.h"
 
 #include "Monarch.g.cpp"
@@ -135,21 +134,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
     // - <none> used
     // Return Value:
     // - <none>
-    winrt::fire_and_forget Monarch::_handleQuitAll(const winrt::Windows::Foundation::IInspectable& /*sender*/,
-                                                   const winrt::Windows::Foundation::IInspectable& /*args*/)
+    void Monarch::_handleQuitAll(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*args*/)
     {
-        // Let the process hosting the monarch run any needed logic before
-        // closing all windows.
-        auto args = winrt::make_self<implementation::QuitAllRequestedArgs>();
-        QuitAllRequested.raise(*this, *args);
-
-        if (const auto action = args->BeforeQuitAllAction())
+        if (_quitting.exchange(true, std::memory_order_relaxed))
         {
-            co_await action;
+            return;
         }
 
-        _quitting.store(true);
-        // Tell all peasants to exit.
         const auto callback = [&](const auto& id, const auto& p) {
             // We want to tell our peasant to quit last, so that we don't try
             // to perform a bunch of elections on quit.
@@ -197,7 +188,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
         // If we are quitting we don't care about maintaining our list of
         // peasants anymore, and don't need to notify the host that something
         // changed.
-        if (_quitting.load(std::memory_order_acquire))
+        if (_quitting.load(std::memory_order_relaxed))
         {
             return;
         }
@@ -1036,30 +1027,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
         _forEachPeasant(func, onError);
     }
 
-    // Method Description:
-    // - Ask all peasants to return their window layout as json
-    // Arguments:
-    // - <none>
-    // Return Value:
-    // - The collection of window layouts from each peasant.
-    Windows::Foundation::Collections::IVector<winrt::hstring> Monarch::GetAllWindowLayouts()
-    {
-        std::vector<winrt::hstring> vec;
-        auto callback = [&](const auto& /*id*/, const auto& p) {
-            vec.emplace_back(p.GetWindowLayout());
-        };
-        auto onError = [](auto&& id) {
-            TraceLoggingWrite(g_hRemotingProvider,
-                              "Monarch_GetAllWindowLayouts_Failed",
-                              TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not get a window layout from"),
-                              TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
-                              TraceLoggingKeyword(TIL_KEYWORD_TRACE));
-        };
-        _forEachPeasant(callback, onError);
-
-        return winrt::single_threaded_vector(std::move(vec));
-    }
-
     void Monarch::RequestMoveContent(winrt::hstring window,
                                      winrt::hstring content,
                                      uint32_t tabIndex,
diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h
index 48619ac47b8..299932d9610 100644
--- a/src/cascadia/Remoting/Monarch.h
+++ b/src/cascadia/Remoting/Monarch.h
@@ -96,7 +96,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
         void SummonAllWindows();
         bool DoesQuakeWindowExist();
         Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo> GetPeasantInfos();
-        Windows::Foundation::Collections::IVector<winrt::hstring> GetAllWindowLayouts();
 
         void RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex, const Windows::Foundation::IReference<Windows::Foundation::Rect>& windowBounds);
         void RequestSendContent(const Remoting::RequestReceiveContentArgs& args);
@@ -106,7 +105,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
         til::typed_event<> HideNotificationIconRequested;
         til::typed_event<> WindowCreated;
         til::typed_event<> WindowClosed;
-        til::typed_event<winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs> QuitAllRequested;
 
         til::typed_event<winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs> RequestNewWindow;
 
@@ -146,8 +144,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
         void _renameRequested(const winrt::Windows::Foundation::IInspectable& sender,
                               const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args);
 
-        winrt::fire_and_forget _handleQuitAll(const winrt::Windows::Foundation::IInspectable& sender,
-                                              const winrt::Windows::Foundation::IInspectable& args);
+        void _handleQuitAll(const winrt::Windows::Foundation::IInspectable& sender,
+                            const winrt::Windows::Foundation::IInspectable& args);
 
         // Method Description:
         // - Helper for doing something on each and every peasant.
diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl
index f57bd6b59b2..c81837e96b5 100644
--- a/src/cascadia/Remoting/Monarch.idl
+++ b/src/cascadia/Remoting/Monarch.idl
@@ -46,11 +46,6 @@ namespace Microsoft.Terminal.Remoting
         Windows.Foundation.IReference<UInt64> WindowID;
     }
 
-    [default_interface] runtimeclass QuitAllRequestedArgs {
-        QuitAllRequestedArgs();
-        Windows.Foundation.IAsyncAction BeforeQuitAllAction;
-    }
-
     struct PeasantInfo
     {
         UInt64 Id;
@@ -72,7 +67,6 @@ namespace Microsoft.Terminal.Remoting
         void SummonAllWindows();
         Boolean DoesQuakeWindowExist();
         Windows.Foundation.Collections.IVectorView<PeasantInfo> GetPeasantInfos { get; };
-        Windows.Foundation.Collections.IVector<String> GetAllWindowLayouts();
 
         void RequestMoveContent(String window, String content, UInt32 tabIndex, Windows.Foundation.IReference<Windows.Foundation.Rect> bounds);
         void RequestSendContent(RequestReceiveContentArgs args);
@@ -82,7 +76,6 @@ namespace Microsoft.Terminal.Remoting
         event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;
         event Windows.Foundation.TypedEventHandler<Object, Object> WindowCreated;
         event Windows.Foundation.TypedEventHandler<Object, Object> WindowClosed;
-        event Windows.Foundation.TypedEventHandler<Object, QuitAllRequestedArgs> QuitAllRequested;
 
         event Windows.Foundation.TypedEventHandler<Object, WindowRequestedArgs> RequestNewWindow;
     };
diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp
index 5d2b54f85af..6378854d199 100644
--- a/src/cascadia/Remoting/Peasant.cpp
+++ b/src/cascadia/Remoting/Peasant.cpp
@@ -5,7 +5,6 @@
 #include "Peasant.h"
 #include "CommandlineArgs.h"
 #include "SummonWindowBehavior.h"
-#include "GetWindowLayoutArgs.h"
 #include "Peasant.g.cpp"
 #include "../../types/inc/utils.hpp"
 #include "AttachRequest.g.cpp"
@@ -309,26 +308,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
                           TraceLoggingKeyword(TIL_KEYWORD_TRACE));
     }
 
-    // Method Description:
-    // - Request and return the window layout from the current TerminalPage
-    // Arguments:
-    // - <none>
-    // Return Value:
-    // - the window layout as a json string
-    hstring Peasant::GetWindowLayout()
-    {
-        auto args = winrt::make_self<implementation::GetWindowLayoutArgs>();
-        GetWindowLayoutRequested.raise(nullptr, *args);
-        if (const auto op = args->WindowLayoutJsonAsync())
-        {
-            // This will fail if called on the UI thread, so the monarch should
-            // never set WindowLayoutJsonAsync.
-            auto str = op.get();
-            return str;
-        }
-        return args->WindowLayoutJson();
-    }
-
     void Peasant::SendContent(const Remoting::RequestReceiveContentArgs& args)
     {
         SendContentRequested.raise(*this, args);
diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h
index 67d407e599a..dc860ba907d 100644
--- a/src/cascadia/Remoting/Peasant.h
+++ b/src/cascadia/Remoting/Peasant.h
@@ -65,7 +65,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
 
         winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs();
 
-        winrt::hstring GetWindowLayout();
         void SendContent(const winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs& args);
 
         til::typed_event<winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs> WindowActivated;
@@ -79,7 +78,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
         til::typed_event<> HideNotificationIconRequested;
         til::typed_event<> QuitAllRequested;
         til::typed_event<> QuitRequested;
-        til::typed_event<winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs> GetWindowLayoutRequested;
 
         til::typed_event<winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::AttachRequest> AttachRequested;
         til::typed_event<winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs> SendContentRequested;
diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl
index f02d22da9c4..3463d561f07 100644
--- a/src/cascadia/Remoting/Peasant.idl
+++ b/src/cascadia/Remoting/Peasant.idl
@@ -32,12 +32,6 @@ namespace Microsoft.Terminal.Remoting
         Windows.Foundation.DateTime ActivatedTime { get; };
     };
 
-    [default_interface] runtimeclass GetWindowLayoutArgs {
-        GetWindowLayoutArgs();
-        String WindowLayoutJson;
-        Windows.Foundation.IAsyncOperation<String> WindowLayoutJsonAsync;
-    }
-
     enum MonitorBehavior
     {
         InPlace,
@@ -88,7 +82,6 @@ namespace Microsoft.Terminal.Remoting
         void RequestHideNotificationIcon();
         void RequestQuitAll();
         void Quit();
-        String GetWindowLayout();
 
         void AttachContentToWindow(AttachRequest request);
         void SendContent(RequestReceiveContentArgs args);
@@ -102,7 +95,6 @@ namespace Microsoft.Terminal.Remoting
 
         event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested;
         event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;
-        event Windows.Foundation.TypedEventHandler<Object, GetWindowLayoutArgs> GetWindowLayoutRequested;
         event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested;
         event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested;
 
diff --git a/src/cascadia/Remoting/QuitAllRequestedArgs.cpp b/src/cascadia/Remoting/QuitAllRequestedArgs.cpp
deleted file mode 100644
index ed5c39dcf4f..00000000000
--- a/src/cascadia/Remoting/QuitAllRequestedArgs.cpp
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (c) Microsoft Corporation.
-// Licensed under the MIT license.
-#include "pch.h"
-#include "QuitAllRequestedArgs.h"
-#include "QuitAllRequestedArgs.g.cpp"
diff --git a/src/cascadia/Remoting/QuitAllRequestedArgs.h b/src/cascadia/Remoting/QuitAllRequestedArgs.h
deleted file mode 100644
index 4fba59cd141..00000000000
--- a/src/cascadia/Remoting/QuitAllRequestedArgs.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*++
-Copyright (c) Microsoft Corporation
-Licensed under the MIT license.
-
-Class Name:
-- QuitAllRequestedArgs.h
-
-Abstract:
-- This is a helper class for allowing the monarch to run code before telling all
-  peasants to quit. This way the monarch can raise an event and get back a future
-  to wait for before continuing.
---*/
-
-#pragma once
-
-#include "QuitAllRequestedArgs.g.h"
-
-namespace winrt::Microsoft::Terminal::Remoting::implementation
-{
-    struct QuitAllRequestedArgs : public QuitAllRequestedArgsT<QuitAllRequestedArgs>
-    {
-        WINRT_PROPERTY(winrt::Windows::Foundation::IAsyncAction, BeforeQuitAllAction, nullptr)
-    };
-}
-
-namespace winrt::Microsoft::Terminal::Remoting::factory_implementation
-{
-    BASIC_FACTORY(QuitAllRequestedArgs);
-}
diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp
index 57c110cd37d..a9d3a42e018 100644
--- a/src/cascadia/Remoting/WindowManager.cpp
+++ b/src/cascadia/Remoting/WindowManager.cpp
@@ -92,7 +92,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
         _monarch.WindowCreated({ get_weak(), &WindowManager::_bubbleWindowCreated });
         _monarch.WindowClosed({ get_weak(), &WindowManager::_bubbleWindowClosed });
         _monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested });
-        _monarch.QuitAllRequested({ get_weak(), &WindowManager::_bubbleQuitAllRequested });
 
         _monarch.RequestNewWindow({ get_weak(), &WindowManager::_raiseRequestNewWindow });
     }
@@ -356,8 +355,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
 
         _monarch.AddPeasant(*p);
 
-        p->GetWindowLayoutRequested({ get_weak(), &WindowManager::_bubbleGetWindowLayoutRequested });
-
         TraceLoggingWrite(g_hRemotingProvider,
                           "WindowManager_CreateOurPeasant",
                           TraceLoggingUInt64(p->GetID(), "peasantID", "The ID of our new peasant"),
@@ -367,10 +364,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
         return *p;
     }
 
-    void WindowManager::_bubbleGetWindowLayoutRequested(const winrt::Windows::Foundation::IInspectable& s, const winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs& e)
-    {
-        GetWindowLayoutRequested.raise(s, e);
-    }
     void WindowManager::_bubbleWindowCreated(const winrt::Windows::Foundation::IInspectable& s, const winrt::Windows::Foundation::IInspectable& e)
     {
         WindowCreated.raise(s, e);
@@ -379,10 +372,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
     {
         WindowClosed.raise(s, e);
     }
-    void WindowManager::_bubbleQuitAllRequested(const winrt::Windows::Foundation::IInspectable& s, const winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs& e)
-    {
-        QuitAllRequested.raise(s, e);
-    }
 
     void WindowManager::SignalClose(const Remoting::Peasant& peasant)
     {
@@ -429,18 +418,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
         return 0;
     }
 
-    // Method Description:
-    // - Ask the monarch to quit all windows.
-    // Arguments:
-    // - <none>
-    // Return Value:
-    // - <none>
-    winrt::fire_and_forget WindowManager::RequestQuitAll(Remoting::Peasant peasant)
-    {
-        co_await winrt::resume_background();
-        peasant.RequestQuitAll();
-    }
-
     bool WindowManager::DoesQuakeWindowExist()
     {
         return _monarch.DoesQuakeWindowExist();
@@ -451,19 +428,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
         winrt::get_self<implementation::Peasant>(peasant)->ActiveTabTitle(title);
     }
 
-    Windows::Foundation::Collections::IVector<winrt::hstring> WindowManager::GetAllWindowLayouts()
-    {
-        if (_monarch)
-        {
-            try
-            {
-                return _monarch.GetAllWindowLayouts();
-            }
-            CATCH_LOG()
-        }
-        return nullptr;
-    }
-
     winrt::fire_and_forget WindowManager::RequestMoveContent(winrt::hstring window,
                                                              winrt::hstring content,
                                                              uint32_t tabIndex,
diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h
index 96946f1d3cc..6dd935e8a65 100644
--- a/src/cascadia/Remoting/WindowManager.h
+++ b/src/cascadia/Remoting/WindowManager.h
@@ -38,10 +38,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
 
         uint64_t GetNumberOfPeasants();
 
-        static winrt::fire_and_forget RequestQuitAll(Remoting::Peasant peasant);
         void UpdateActiveTabTitle(const winrt::hstring& title, const Remoting::Peasant& peasant);
 
-        Windows::Foundation::Collections::IVector<winrt::hstring> GetAllWindowLayouts();
         bool DoesQuakeWindowExist();
 
         winrt::fire_and_forget RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex, Windows::Foundation::IReference<Windows::Foundation::Rect> windowBounds);
@@ -51,8 +49,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
 
         til::typed_event<> WindowCreated;
         til::typed_event<> WindowClosed;
-        til::typed_event<winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs> QuitAllRequested;
-        til::typed_event<winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs> GetWindowLayoutRequested;
         til::typed_event<winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs> RequestNewWindow;
 
     private:
@@ -71,8 +67,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
                                     const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args);
         void _bubbleWindowCreated(const winrt::Windows::Foundation::IInspectable& s, const winrt::Windows::Foundation::IInspectable& e);
         void _bubbleWindowClosed(const winrt::Windows::Foundation::IInspectable& s, const winrt::Windows::Foundation::IInspectable& e);
-        void _bubbleQuitAllRequested(const winrt::Windows::Foundation::IInspectable& s, const winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs& e);
-        void _bubbleGetWindowLayoutRequested(const winrt::Windows::Foundation::IInspectable& s, const winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs& e);
     };
 }
 
diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl
index 5dd72b7f19a..deaf2b7489e 100644
--- a/src/cascadia/Remoting/WindowManager.idl
+++ b/src/cascadia/Remoting/WindowManager.idl
@@ -14,12 +14,10 @@ namespace Microsoft.Terminal.Remoting
         void SignalClose(Peasant p);
 
         void UpdateActiveTabTitle(String title, Peasant p);
-        static void RequestQuitAll(Peasant p);
 
         void SummonWindow(SummonWindowSelectionArgs args);
         void SummonAllWindows();
 
-        Windows.Foundation.Collections.IVector<String> GetAllWindowLayouts();
         Windows.Foundation.Collections.IVectorView<PeasantInfo> GetPeasantInfos();
 
         UInt64 GetNumberOfPeasants();
@@ -33,8 +31,6 @@ namespace Microsoft.Terminal.Remoting
 
         event Windows.Foundation.TypedEventHandler<Object, Object> WindowCreated;
         event Windows.Foundation.TypedEventHandler<Object, Object> WindowClosed;
-        event Windows.Foundation.TypedEventHandler<Object, QuitAllRequestedArgs> QuitAllRequested;
-        event Windows.Foundation.TypedEventHandler<Object, GetWindowLayoutArgs> GetWindowLayoutRequested;
 
         event Windows.Foundation.TypedEventHandler<Object, WindowRequestedArgs> RequestNewWindow;
 
diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp
index 5060b4ff2f7..b7a6b581a05 100644
--- a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp
+++ b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp
@@ -549,6 +549,9 @@ void AppCommandlineArgs::_addNewTerminalArgs(AppCommandlineArgs::NewTerminalSubc
     subcommand.profileNameOption = subcommand.subcommand->add_option("-p,--profile",
                                                                      _profileName,
                                                                      RS_A(L"CmdProfileArgDesc"));
+    subcommand.sessionIdOption = subcommand.subcommand->add_option("--sessionId",
+                                                                   _sessionId,
+                                                                   RS_A(L"CmdSessionIdArgDesc"));
     subcommand.startingDirectoryOption = subcommand.subcommand->add_option("-d,--startingDirectory",
                                                                            _startingDirectory,
                                                                            RS_A(L"CmdStartingDirArgDesc"));
@@ -628,6 +631,13 @@ NewTerminalArgs AppCommandlineArgs::_getNewTerminalArgs(AppCommandlineArgs::NewT
         args.Profile(winrt::to_hstring(_profileName));
     }
 
+    if (*subcommand.sessionIdOption)
+    {
+        const auto str = winrt::to_hstring(_sessionId);
+        const auto id = ::Microsoft::Console::Utils::GuidFromString(str.c_str());
+        args.SessionId(id);
+    }
+
     if (*subcommand.startingDirectoryOption)
     {
         args.StartingDirectory(winrt::to_hstring(_startingDirectory));
@@ -714,6 +724,7 @@ bool AppCommandlineArgs::_noCommandsProvided()
 void AppCommandlineArgs::_resetStateToDefault()
 {
     _profileName.clear();
+    _sessionId.clear();
     _startingDirectory.clear();
     _startingTitle.clear();
     _startingTabColor.clear();
diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.h b/src/cascadia/TerminalApp/AppCommandlineArgs.h
index 9145697f453..7eb5a34f93d 100644
--- a/src/cascadia/TerminalApp/AppCommandlineArgs.h
+++ b/src/cascadia/TerminalApp/AppCommandlineArgs.h
@@ -62,6 +62,7 @@ class TerminalApp::AppCommandlineArgs final
         CLI::App* subcommand;
         CLI::Option* commandlineOption;
         CLI::Option* profileNameOption;
+        CLI::Option* sessionIdOption;
         CLI::Option* startingDirectoryOption;
         CLI::Option* titleOption;
         CLI::Option* tabColorOption;
@@ -96,6 +97,7 @@ class TerminalApp::AppCommandlineArgs final
     // Are you adding a new sub-command? Make sure to update _noCommandsProvided!
 
     std::string _profileName;
+    std::string _sessionId;
     std::string _startingDirectory;
     std::string _startingTitle;
     std::string _startingTabColor;
diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp
index 0b3ada01efa..dc11b506449 100644
--- a/src/cascadia/TerminalApp/AppLogic.cpp
+++ b/src/cascadia/TerminalApp/AppLogic.cpp
@@ -702,22 +702,6 @@ namespace winrt::TerminalApp::implementation
         return _settings.GlobalSettings().ShouldUsePersistedLayout();
     }
 
-    void AppLogic::SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector<hstring>& layouts)
-    {
-        std::vector<WindowLayout> converted;
-        converted.reserve(layouts.Size());
-
-        for (const auto& json : layouts)
-        {
-            if (json != L"")
-            {
-                converted.emplace_back(WindowLayout::FromJson(json));
-            }
-        }
-
-        ApplicationState::SharedInstance().PersistedWindowLayouts(winrt::single_threaded_vector(std::move(converted)));
-    }
-
     TerminalApp::ParseCommandlineResult AppLogic::GetParseCommandlineMessage(array_view<const winrt::hstring> args)
     {
         ::TerminalApp::AppCommandlineArgs _appArgs;
diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h
index 3c14a4bca41..43d64184deb 100644
--- a/src/cascadia/TerminalApp/AppLogic.h
+++ b/src/cascadia/TerminalApp/AppLogic.h
@@ -53,9 +53,7 @@ namespace winrt::TerminalApp::implementation
         void NotifyRootInitialized();
 
         bool HasSettingsStartupActions() const noexcept;
-
         bool ShouldUsePersistedLayout() const;
-        void SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector<hstring>& layouts);
 
         [[nodiscard]] Microsoft::Terminal::Settings::Model::CascadiaSettings GetSettings() const noexcept;
 
diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl
index 1021e61b99a..362c7405644 100644
--- a/src/cascadia/TerminalApp/AppLogic.idl
+++ b/src/cascadia/TerminalApp/AppLogic.idl
@@ -37,7 +37,6 @@ namespace TerminalApp
         Boolean HasSettingsStartupActions();
 
         Boolean ShouldUsePersistedLayout();
-        void SaveWindowLayoutJsons(Windows.Foundation.Collections.IVector<String> layouts);
 
         void ReloadSettings();
 
diff --git a/src/cascadia/TerminalApp/IPaneContent.idl b/src/cascadia/TerminalApp/IPaneContent.idl
index ebe0914ea9c..a26d1a05477 100644
--- a/src/cascadia/TerminalApp/IPaneContent.idl
+++ b/src/cascadia/TerminalApp/IPaneContent.idl
@@ -3,6 +3,13 @@
 
 namespace TerminalApp
 {
+    enum BuildStartupKind
+    {
+        None,
+        Content,
+        MovePane,
+        Persist,
+    };
 
     runtimeclass BellEventArgs
     {
@@ -20,7 +27,7 @@ namespace TerminalApp
         UInt64 TaskbarProgress { get; };
         Boolean ReadOnly { get; };
 
-        Microsoft.Terminal.Settings.Model.NewTerminalArgs GetNewTerminalArgs(Boolean asContent);
+        Microsoft.Terminal.Settings.Model.NewTerminalArgs GetNewTerminalArgs(BuildStartupKind kind);
 
         void Focus(Windows.UI.Xaml.FocusState reason);
 
diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp
index 9ed53d49204..45950e2c776 100644
--- a/src/cascadia/TerminalApp/Pane.cpp
+++ b/src/cascadia/TerminalApp/Pane.cpp
@@ -4,12 +4,6 @@
 #include "pch.h"
 #include "Pane.h"
 
-#include "AppLogic.h"
-
-#include "Utils.h"
-
-#include <Mmsystem.h>
-
 using namespace winrt::Windows::Foundation;
 using namespace winrt::Windows::Graphics::Display;
 using namespace winrt::Windows::UI;
@@ -20,7 +14,6 @@ using namespace winrt::Microsoft::Terminal::Settings::Model;
 using namespace winrt::Microsoft::Terminal::Control;
 using namespace winrt::Microsoft::Terminal::TerminalConnection;
 using namespace winrt::TerminalApp;
-using namespace TerminalApp;
 
 static const int PaneBorderSize = 2;
 static const int CombinedPaneBorderSize = 2 * PaneBorderSize;
@@ -105,21 +98,13 @@ Pane::Pane(std::shared_ptr<Pane> first,
     });
 }
 
-// Method Description:
-// - Extract the terminal settings from the current (leaf) pane's control
-//   to be used to create an equivalent control
-// Arguments:
-// - asContent: when true, we're trying to serialize this pane for moving across
-//   windows. In that case, we'll need to fill in the content guid for our new
-//   terminal args.
-// Return Value:
-// - Arguments appropriate for a SplitPane or NewTab action
-NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const
+// Extract the terminal settings from the current (leaf) pane's control
+// to be used to create an equivalent control
+NewTerminalArgs Pane::GetTerminalArgsForPane(BuildStartupKind kind) const
 {
     // Leaves are the only things that have controls
     assert(_IsLeaf());
-
-    return _content.GetNewTerminalArgs(asContent);
+    return _content.GetNewTerminalArgs(kind);
 }
 
 // Method Description:
@@ -142,16 +127,13 @@ NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const
 // - The state from building the startup actions, includes a vector of commands,
 //   the original root pane, the id of the focused pane, and the number of panes
 //   created.
-Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId,
-                                                  uint32_t nextId,
-                                                  const bool asContent,
-                                                  const bool asMovePane)
+Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t nextId, BuildStartupKind kind)
 {
     // Normally, if we're a leaf, return an empt set of actions, because the
     // parent pane will build the SplitPane action for us. If we're building
     // actions for a movePane action though, we'll still need to include
     // ourselves.
-    if (!asMovePane && _IsLeaf())
+    if (kind != BuildStartupKind::MovePane && _IsLeaf())
     {
         if (_lastActive)
         {
@@ -165,18 +147,18 @@ Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId,
     auto buildSplitPane = [&](auto newPane) {
         ActionAndArgs actionAndArgs;
         actionAndArgs.Action(ShortcutAction::SplitPane);
-        const auto terminalArgs{ newPane->GetTerminalArgsForPane(asContent) };
+        const auto terminalArgs{ newPane->GetTerminalArgsForPane(kind) };
         // When creating a pane the split size is the size of the new pane
         // and not position.
         const auto splitDirection = _splitState == SplitState::Horizontal ? SplitDirection::Down : SplitDirection::Right;
-        const auto splitSize = (asContent && _IsLeaf() ? .5 : 1. - _desiredSplitPosition);
+        const auto splitSize = (kind != BuildStartupKind::None && _IsLeaf() ? .5 : 1. - _desiredSplitPosition);
         SplitPaneArgs args{ SplitType::Manual, splitDirection, splitSize, terminalArgs };
         actionAndArgs.Args(args);
 
         return actionAndArgs;
     };
 
-    if (asContent && _IsLeaf())
+    if (kind != BuildStartupKind::None && _IsLeaf())
     {
         return {
             .args = { buildSplitPane(shared_from_this()) },
@@ -223,10 +205,10 @@ Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId,
     // We now need to execute the commands for each side of the tree
     // We've done one split, so the first-most child will have currentId, and the
     // one after it will be incremented.
-    auto firstState = _firstChild->BuildStartupActions(currentId, nextId + 1);
+    auto firstState = _firstChild->BuildStartupActions(currentId, nextId + 1, kind);
     // the next id for the second branch depends on how many splits were in the
     // first child.
-    auto secondState = _secondChild->BuildStartupActions(nextId, nextId + firstState.panesCreated + 1);
+    auto secondState = _secondChild->BuildStartupActions(nextId, nextId + firstState.panesCreated + 1, kind);
 
     std::vector<ActionAndArgs> actions{};
     actions.reserve(firstState.args.size() + secondState.args.size() + 3);
diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h
index 1f237f83190..0f4a5c907dc 100644
--- a/src/cascadia/TerminalApp/Pane.h
+++ b/src/cascadia/TerminalApp/Pane.h
@@ -104,8 +104,8 @@ class Pane : public std::enable_shared_from_this<Pane>
         std::optional<uint32_t> focusedPaneId;
         uint32_t panesCreated;
     };
-    BuildStartupState BuildStartupActions(uint32_t currentId, uint32_t nextId, const bool asContent = false, const bool asMovePane = false);
-    winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetTerminalArgsForPane(const bool asContent = false) const;
+    BuildStartupState BuildStartupActions(uint32_t currentId, uint32_t nextId, winrt::TerminalApp::BuildStartupKind kind);
+    winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetTerminalArgsForPane(winrt::TerminalApp::BuildStartupKind kind) const;
 
     void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings,
                         const winrt::Microsoft::Terminal::Settings::Model::Profile& profile);
diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw
index 3f91408815f..54cd9e9bc30 100644
--- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw
+++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw
@@ -187,9 +187,6 @@
   <data name="Quit" xml:space="preserve">
     <value>Quit</value>
   </data>
-  <data name="CloseWindowWarningTitle" xml:space="preserve">
-    <value>Do you want to close all tabs?</value>
-  </data>
   <data name="MultiplePanes" xml:space="preserve">
     <value>Multiple panes</value>
   </data>
@@ -340,6 +337,9 @@
   <data name="CmdProfileArgDesc" xml:space="preserve">
     <value>Open with the given profile. Accepts either the name or GUID of a profile</value>
   </data>
+  <data name="CmdSessionIdArgDesc" xml:space="preserve">
+    <value>Sets the WT_SESSION variable; must be a GUID</value>
+  </data>
   <data name="CmdSplitPaneDesc" xml:space="preserve">
     <value>Create a new split pane</value>
   </data>
diff --git a/src/cascadia/TerminalApp/ScratchpadContent.cpp b/src/cascadia/TerminalApp/ScratchpadContent.cpp
index dcb13697b54..0ed99203391 100644
--- a/src/cascadia/TerminalApp/ScratchpadContent.cpp
+++ b/src/cascadia/TerminalApp/ScratchpadContent.cpp
@@ -43,7 +43,7 @@ namespace winrt::TerminalApp::implementation
         CloseRequested.raise(*this, nullptr);
     }
 
-    NewTerminalArgs ScratchpadContent::GetNewTerminalArgs(const bool /* asContent */) const
+    NewTerminalArgs ScratchpadContent::GetNewTerminalArgs(const BuildStartupKind /* kind */) const
     {
         return nullptr;
     }
diff --git a/src/cascadia/TerminalApp/ScratchpadContent.h b/src/cascadia/TerminalApp/ScratchpadContent.h
index 10aa36b6b85..a2b76e47934 100644
--- a/src/cascadia/TerminalApp/ScratchpadContent.h
+++ b/src/cascadia/TerminalApp/ScratchpadContent.h
@@ -17,7 +17,7 @@ namespace winrt::TerminalApp::implementation
 
         void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic);
         void Close();
-        winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(const bool asContent) const;
+        winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(BuildStartupKind kind) const;
 
         winrt::hstring Title() { return L"Scratchpad"; }
         uint64_t TaskbarState() { return 0; }
diff --git a/src/cascadia/TerminalApp/SettingsTab.cpp b/src/cascadia/TerminalApp/SettingsTab.cpp
index 12107e8efea..483f4f30475 100644
--- a/src/cascadia/TerminalApp/SettingsTab.cpp
+++ b/src/cascadia/TerminalApp/SettingsTab.cpp
@@ -57,7 +57,7 @@ namespace winrt::TerminalApp::implementation
     //   re-evaluate including that arg in this action then.
     //  Return Value:
     // - The list of actions.
-    std::vector<ActionAndArgs> SettingsTab::BuildStartupActions(const bool /*asContent*/) const
+    std::vector<ActionAndArgs> SettingsTab::BuildStartupActions(BuildStartupKind) const
     {
         ASSERT_UI_THREAD();
 
diff --git a/src/cascadia/TerminalApp/SettingsTab.h b/src/cascadia/TerminalApp/SettingsTab.h
index 657c8387661..e5d7df92faf 100644
--- a/src/cascadia/TerminalApp/SettingsTab.h
+++ b/src/cascadia/TerminalApp/SettingsTab.h
@@ -30,7 +30,7 @@ namespace winrt::TerminalApp::implementation
         void UpdateSettings(Microsoft::Terminal::Settings::Model::CascadiaSettings settings);
         void Focus(winrt::Windows::UI::Xaml::FocusState focusState) override;
 
-        std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions(const bool asContent = false) const override;
+        std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions(BuildStartupKind kind) const override;
 
     private:
         winrt::Windows::UI::Xaml::ElementTheme _requestedTheme;
diff --git a/src/cascadia/TerminalApp/TabBase.h b/src/cascadia/TerminalApp/TabBase.h
index 8a0190ad3c3..9a538550b8d 100644
--- a/src/cascadia/TerminalApp/TabBase.h
+++ b/src/cascadia/TerminalApp/TabBase.h
@@ -22,7 +22,7 @@ namespace winrt::TerminalApp::implementation
 
         void UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs);
         void SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap);
-        virtual std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions(const bool asContent = false) const = 0;
+        virtual std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions(BuildStartupKind kind) const = 0;
 
         virtual std::optional<winrt::Windows::UI::Color> GetTabColor();
         void ThemeColor(const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& focused,
diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp
index b407c6107c1..8d99ac600f5 100644
--- a/src/cascadia/TerminalApp/TabManagement.cpp
+++ b/src/cascadia/TerminalApp/TabManagement.cpp
@@ -429,7 +429,7 @@ namespace winrt::TerminalApp::implementation
         }
 
         auto t = winrt::get_self<implementation::TabBase>(tab);
-        auto actions = t->BuildStartupActions();
+        auto actions = t->BuildStartupActions(BuildStartupKind::None);
         _AddPreviouslyClosedPaneOrTab(std::move(actions));
 
         _RemoveTab(tab);
@@ -486,7 +486,7 @@ namespace winrt::TerminalApp::implementation
             // if the user manually closed all tabs.
             // Do this only if we are the last window; the monarch will notice
             // we are missing and remove us that way otherwise.
-            LastTabClosed.raise(*this, winrt::make<LastTabClosedEventArgs>(!_maintainStateOnTabClose));
+            CloseWindowRequested.raise(*this, nullptr);
         }
         else if (focusedTabIndex.has_value() && focusedTabIndex.value() == gsl::narrow_cast<uint32_t>(tabIndex))
         {
@@ -767,11 +767,11 @@ namespace winrt::TerminalApp::implementation
         // This doesn't handle refocusing anything in particular, the
         // result will be that the last pane created is focused. In the
         // case of a single pane that is the desired behavior anyways.
-        auto state = pane->BuildStartupActions(0, 1);
+        auto state = pane->BuildStartupActions(0, 1, BuildStartupKind::None);
         {
             ActionAndArgs splitPaneAction{};
             splitPaneAction.Action(ShortcutAction::SplitPane);
-            SplitPaneArgs splitPaneArgs{ SplitDirection::Automatic, state.firstPane->GetTerminalArgsForPane() };
+            SplitPaneArgs splitPaneArgs{ SplitDirection::Automatic, state.firstPane->GetTerminalArgsForPane(BuildStartupKind::None) };
             splitPaneAction.Args(splitPaneArgs);
 
             state.args.emplace(state.args.begin(), std::move(splitPaneAction));
@@ -1139,12 +1139,4 @@ namespace winrt::TerminalApp::implementation
     {
         return _tabs.Size() > 1;
     }
-
-    void TerminalPage::_RemoveAllTabs()
-    {
-        // Since _RemoveTabs is asynchronous, create a snapshot of the  tabs we want to remove
-        std::vector<winrt::TerminalApp::TabBase> tabsToRemove;
-        std::copy(begin(_tabs), end(_tabs), std::back_inserter(tabsToRemove));
-        _RemoveTabs(tabsToRemove);
-    }
 }
diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp
index d3fc7a2dc25..591fc6906a0 100644
--- a/src/cascadia/TerminalApp/TerminalPage.cpp
+++ b/src/cascadia/TerminalApp/TerminalPage.cpp
@@ -5,7 +5,6 @@
 #include "pch.h"
 #include "TerminalPage.h"
 #include "TerminalPage.g.cpp"
-#include "LastTabClosedEventArgs.g.cpp"
 #include "RenameWindowRequestedArgs.g.cpp"
 #include "RequestMoveContentArgs.g.cpp"
 #include "RequestReceiveContentArgs.g.cpp"
@@ -654,9 +653,9 @@ namespace winrt::TerminalApp::implementation
         // GH#12267: Make sure that we don't instantly close ourselves when
         // we're readying to accept a defterm connection. In that case, we don't
         // have a tab yet, but will once we're initialized.
-        if (_tabs.Size() == 0 && !(_shouldStartInboundListener || _isEmbeddingInboundListener))
+        if (_tabs.Size() == 0 && !_shouldStartInboundListener && !_isEmbeddingInboundListener)
         {
-            LastTabClosed.raise(*this, winrt::make<LastTabClosedEventArgs>(false));
+            CloseWindowRequested.raise(*this, nullptr);
             co_return;
         }
         else
@@ -1277,6 +1276,11 @@ namespace winrt::TerminalApp::implementation
             }
         }
 
+        if (const auto id = settings.SessionId(); id != winrt::guid{})
+        {
+            valueSet.Insert(L"sessionId", Windows::Foundation::PropertyValue::CreateGuid(id));
+        }
+
         connection.Initialize(valueSet);
 
         TraceLoggingWrite(
@@ -1891,19 +1895,11 @@ namespace winrt::TerminalApp::implementation
         }
     }
 
-    // Method Description:
-    // - Saves the window position and tab layout to the application state
-    // - This does not create the InitialPosition field, that needs to be
-    //   added externally.
-    // Arguments:
-    // - <none>
-    // Return Value:
-    // - the window layout
-    WindowLayout TerminalPage::GetWindowLayout()
+    void TerminalPage::PersistState()
     {
         if (_startupState != StartupState::Initialized)
         {
-            return nullptr;
+            return;
         }
 
         std::vector<ActionAndArgs> actions;
@@ -1911,7 +1907,7 @@ namespace winrt::TerminalApp::implementation
         for (auto tab : _tabs)
         {
             auto t = winrt::get_self<implementation::TabBase>(tab);
-            auto tabActions = t->BuildStartupActions();
+            auto tabActions = t->BuildStartupActions(BuildStartupKind::Persist);
             actions.insert(actions.end(), std::make_move_iterator(tabActions.begin()), std::make_move_iterator(tabActions.end()));
         }
 
@@ -1938,7 +1934,7 @@ namespace winrt::TerminalApp::implementation
             actions.emplace_back(std::move(action));
         }
 
-        WindowLayout layout{};
+        WindowLayout layout;
         layout.TabLayout(winrt::single_threaded_vector<ActionAndArgs>(std::move(actions)));
 
         auto mode = LaunchMode::DefaultMode;
@@ -1955,20 +1951,15 @@ namespace winrt::TerminalApp::implementation
 
         layout.InitialSize(windowSize);
 
-        return layout;
+        ApplicationState::SharedInstance().AppendPersistedWindowLayout(layout);
     }
 
     // Method Description:
     // - Close the terminal app. If there is more
     //   than one tab opened, show a warning dialog.
-    // Arguments:
-    // - bypassDialog: if true a dialog won't be shown even if the user would
-    //   normally get confirmation. This is used in the case where the user
-    //   has already been prompted by the Quit action.
-    fire_and_forget TerminalPage::CloseWindow(bool bypassDialog)
+    fire_and_forget TerminalPage::CloseWindow()
     {
-        if (!bypassDialog &&
-            _HasMultipleTabs() &&
+        if (_HasMultipleTabs() &&
             _settings.GlobalSettings().ConfirmCloseAllTabs() &&
             !_displayingCloseDialog)
         {
@@ -1987,15 +1978,7 @@ namespace winrt::TerminalApp::implementation
             }
         }
 
-        if (_settings.GlobalSettings().ShouldUsePersistedLayout())
-        {
-            // Don't delete the ApplicationState when all of the tabs are removed.
-            // If there is still a monarch living they will get the event that
-            // a window closed and trigger a new save without this window.
-            _maintainStateOnTabClose = true;
-        }
-
-        _RemoveAllTabs();
+        CloseWindowRequested.raise(*this, nullptr);
     }
 
     // Method Description:
@@ -2057,7 +2040,7 @@ namespace winrt::TerminalApp::implementation
             {
                 if (const auto pane{ terminalTab->GetActivePane() })
                 {
-                    auto startupActions = pane->BuildStartupActions(0, 1, true, true);
+                    auto startupActions = pane->BuildStartupActions(0, 1, BuildStartupKind::MovePane);
                     _DetachPaneFromWindow(pane);
                     _MoveContent(std::move(startupActions.args), windowId, tabIdx);
                     focusedTab->DetachPane();
@@ -2199,7 +2182,7 @@ namespace winrt::TerminalApp::implementation
 
             if (tab)
             {
-                auto startupActions = tab->BuildStartupActions(true);
+                auto startupActions = tab->BuildStartupActions(BuildStartupKind::Content);
                 _DetachTabFromWindow(tab);
                 _MoveContent(std::move(startupActions), windowId, 0);
                 _RemoveTab(*tab);
@@ -3041,7 +3024,17 @@ namespace winrt::TerminalApp::implementation
         // TermControl will copy the settings out of the settings passed to it.
 
         const auto content = _manager.CreateCore(settings.DefaultSettings(), settings.UnfocusedSettings(), connection);
-        return _SetupControl(TermControl{ content });
+        const TermControl control{ content };
+
+        if (const auto id = settings.DefaultSettings().SessionId(); id != winrt::guid{})
+        {
+            const auto settingsDir = CascadiaSettings::SettingsDirectory();
+            const auto idStr = Utils::GuidToPlainString(id);
+            const auto path = fmt::format(FMT_COMPILE(L"{}\\buffer_{}.txt"), settingsDir, idStr);
+            control.RestoreFromPath(path);
+        }
+
+        return _SetupControl(control);
     }
 
     TermControl TerminalPage::_AttachControlToContent(const uint64_t& contentId)
@@ -5124,7 +5117,7 @@ namespace winrt::TerminalApp::implementation
                                                const uint32_t tabIndex,
                                                std::optional<til::point> dragPoint)
     {
-        auto startupActions = _stashed.draggedTab->BuildStartupActions(true);
+        auto startupActions = _stashed.draggedTab->BuildStartupActions(BuildStartupKind::Content);
         _DetachTabFromWindow(_stashed.draggedTab);
 
         _MoveContent(std::move(startupActions), windowId, tabIndex, dragPoint);
diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h
index 639165d1730..227f28edbb5 100644
--- a/src/cascadia/TerminalApp/TerminalPage.h
+++ b/src/cascadia/TerminalApp/TerminalPage.h
@@ -7,7 +7,6 @@
 #include "TerminalTab.h"
 #include "AppKeyBindings.h"
 #include "AppCommandlineArgs.h"
-#include "LastTabClosedEventArgs.g.h"
 #include "RenameWindowRequestedArgs.g.h"
 #include "RequestMoveContentArgs.g.h"
 #include "RequestReceiveContentArgs.g.h"
@@ -44,15 +43,6 @@ namespace winrt::TerminalApp::implementation
         ScrollDown = 1
     };
 
-    struct LastTabClosedEventArgs : LastTabClosedEventArgsT<LastTabClosedEventArgs>
-    {
-        WINRT_PROPERTY(bool, ClearPersistedState);
-
-    public:
-        LastTabClosedEventArgs(const bool& shouldClear) :
-            _ClearPersistedState{ shouldClear } {};
-    };
-
     struct RenameWindowRequestedArgs : RenameWindowRequestedArgsT<RenameWindowRequestedArgs>
     {
         WINRT_PROPERTY(winrt::hstring, ProposedName);
@@ -105,7 +95,6 @@ namespace winrt::TerminalApp::implementation
 
         bool ShouldImmediatelyHandoffToElevated(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const;
         void HandoffToElevated(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings);
-        Microsoft::Terminal::Settings::Model::WindowLayout GetWindowLayout();
 
         hstring Title();
 
@@ -121,7 +110,8 @@ namespace winrt::TerminalApp::implementation
         SuggestionsControl LoadSuggestionsUI();
 
         winrt::fire_and_forget RequestQuit();
-        winrt::fire_and_forget CloseWindow(bool bypassDialog);
+        winrt::fire_and_forget CloseWindow();
+        void PersistState();
 
         void ToggleFocusMode();
         void ToggleFullscreen();
@@ -175,7 +165,7 @@ namespace winrt::TerminalApp::implementation
 
         // -------------------------------- WinRT Events ---------------------------------
         til::typed_event<IInspectable, winrt::hstring> TitleChanged;
-        til::typed_event<IInspectable, winrt::TerminalApp::LastTabClosedEventArgs> LastTabClosed;
+        til::typed_event<IInspectable, IInspectable> CloseWindowRequested;
         til::typed_event<IInspectable, winrt::Windows::UI::Xaml::UIElement> SetTitleBarContent;
         til::typed_event<IInspectable, IInspectable> FocusModeChanged;
         til::typed_event<IInspectable, IInspectable> FullscreenChanged;
@@ -185,7 +175,7 @@ namespace winrt::TerminalApp::implementation
         til::typed_event<IInspectable, IInspectable> SetTaskbarProgress;
         til::typed_event<IInspectable, IInspectable> Initialized;
         til::typed_event<IInspectable, IInspectable> IdentifyWindowsRequested;
-        til::typed_event<Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs> RenameWindowRequested;
+        til::typed_event<IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs> RenameWindowRequested;
         til::typed_event<IInspectable, IInspectable> SummonWindowRequested;
 
         til::typed_event<IInspectable, IInspectable> CloseRequested;
@@ -232,7 +222,6 @@ namespace winrt::TerminalApp::implementation
 
         std::optional<uint32_t> _loadFromPersistedLayoutIdx{};
 
-        bool _maintainStateOnTabClose{ false };
         bool _rearranging{ false };
         std::optional<int> _rearrangeFrom{};
         std::optional<int> _rearrangeTo{};
@@ -348,7 +337,6 @@ namespace winrt::TerminalApp::implementation
         void _DismissTabContextMenus();
         void _FocusCurrentTab(const bool focusAlways);
         bool _HasMultipleTabs() const;
-        void _RemoveAllTabs();
 
         void _SelectNextTab(const bool bMoveRight, const Windows::Foundation::IReference<Microsoft::Terminal::Settings::Model::TabSwitcherMode>& customTabSwitcherMode);
         bool _SelectTab(uint32_t tabIndex);
diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl
index f322c0d527e..8ec4fa1dc9c 100644
--- a/src/cascadia/TerminalApp/TerminalPage.idl
+++ b/src/cascadia/TerminalApp/TerminalPage.idl
@@ -14,11 +14,6 @@ namespace TerminalApp
         void Detach(Microsoft.Terminal.Control.TermControl control);
     }
 
-    [default_interface] runtimeclass LastTabClosedEventArgs
-    {
-        Boolean ClearPersistedState { get; };
-    };
-
     [default_interface] runtimeclass RenameWindowRequestedArgs
     {
         String ProposedName { get; };
@@ -86,7 +81,7 @@ namespace TerminalApp
         void SendContentToOther(RequestReceiveContentArgs args);
 
         event Windows.Foundation.TypedEventHandler<Object, String> TitleChanged;
-        event Windows.Foundation.TypedEventHandler<Object, LastTabClosedEventArgs> LastTabClosed;
+        event Windows.Foundation.TypedEventHandler<Object, Object> CloseWindowRequested;
         event Windows.Foundation.TypedEventHandler<Object, Windows.UI.Xaml.UIElement> SetTitleBarContent;
         event Windows.Foundation.TypedEventHandler<Object, Object> FocusModeChanged;
         event Windows.Foundation.TypedEventHandler<Object, Object> FullscreenChanged;
diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp
index 597631d8fdc..49bbe3ff355 100644
--- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp
+++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp
@@ -3,11 +3,14 @@
 
 #include "pch.h"
 #include "TerminalPaneContent.h"
-#include "TerminalPaneContent.g.cpp"
+
+#include <mmsystem.h>
+
+#include "../../types/inc/utils.hpp"
 
 #include "BellEventArgs.g.cpp"
+#include "TerminalPaneContent.g.cpp"
 
-#include <Mmsystem.h>
 using namespace winrt::Windows::Foundation;
 using namespace winrt::Windows::UI::Xaml;
 using namespace winrt::Microsoft::Terminal::Settings::Model;
@@ -77,7 +80,7 @@ namespace winrt::TerminalApp::implementation
         CloseRequested.raise(*this, nullptr);
     }
 
-    NewTerminalArgs TerminalPaneContent::GetNewTerminalArgs(const bool asContent) const
+    NewTerminalArgs TerminalPaneContent::GetNewTerminalArgs(const BuildStartupKind kind) const
     {
         NewTerminalArgs args{};
         const auto& controlSettings = _control.Settings();
@@ -119,12 +122,32 @@ namespace winrt::TerminalApp::implementation
         // object. That would work for schemes set by the Terminal, but not ones set
         // by VT, but that seems good enough.
 
-        // Only fill in the ContentId if absolutely needed. If you fill in a number
-        // here (even 0), we'll serialize that number, AND treat that action as an
-        // "attach existing" rather than a "create"
-        if (asContent)
+        switch (kind)
         {
+        case BuildStartupKind::Content:
+        case BuildStartupKind::MovePane:
+            // Only fill in the ContentId if absolutely needed. If you fill in a number
+            // here (even 0), we'll serialize that number, AND treat that action as an
+            // "attach existing" rather than a "create"
             args.ContentId(_control.ContentId());
+            break;
+        case BuildStartupKind::Persist:
+        {
+            const auto connection = _control.Connection();
+            const auto id = connection ? connection.SessionId() : winrt::guid{};
+
+            if (id != winrt::guid{})
+            {
+                const auto settingsDir = CascadiaSettings::SettingsDirectory();
+                const auto idStr = ::Microsoft::Console::Utils::GuidToPlainString(id);
+                const auto path = fmt::format(FMT_COMPILE(L"{}\\buffer_{}.txt"), settingsDir, idStr);
+                _control.PersistToPath(path);
+                args.SessionId(id);
+            }
+            break;
+        }
+        default:
+            break;
         }
 
         return args;
diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h
index 2c2deee0124..11ad0425379 100644
--- a/src/cascadia/TerminalApp/TerminalPaneContent.h
+++ b/src/cascadia/TerminalApp/TerminalPaneContent.h
@@ -27,7 +27,7 @@ namespace winrt::TerminalApp::implementation
         void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic);
         void Close();
 
-        winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(const bool asContent) const;
+        winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(BuildStartupKind kind) const;
 
         void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings,
                             const winrt::Microsoft::Terminal::Settings::Model::Profile& profile);
diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp
index c5cb7f5b5f2..247601e21d1 100644
--- a/src/cascadia/TerminalApp/TerminalTab.cpp
+++ b/src/cascadia/TerminalApp/TerminalTab.cpp
@@ -432,18 +432,18 @@ namespace winrt::TerminalApp::implementation
     // - <none>
     // Return Value:
     // - A vector of commands
-    std::vector<ActionAndArgs> TerminalTab::BuildStartupActions(const bool asContent) const
+    std::vector<ActionAndArgs> TerminalTab::BuildStartupActions(BuildStartupKind kind) const
     {
         ASSERT_UI_THREAD();
 
         // Give initial ids (0 for the child created with this tab,
         // 1 for the child after the first split.
-        auto state = _rootPane->BuildStartupActions(0, 1, asContent);
+        auto state = _rootPane->BuildStartupActions(0, 1, kind);
 
         {
             ActionAndArgs newTabAction{};
             newTabAction.Action(ShortcutAction::NewTab);
-            NewTabArgs newTabArgs{ state.firstPane->GetTerminalArgsForPane(asContent) };
+            NewTabArgs newTabArgs{ state.firstPane->GetTerminalArgsForPane(kind) };
             newTabAction.Args(newTabArgs);
 
             state.args.emplace(state.args.begin(), std::move(newTabAction));
diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h
index df9e4cae825..3ae1a3cc2fa 100644
--- a/src/cascadia/TerminalApp/TerminalTab.h
+++ b/src/cascadia/TerminalApp/TerminalTab.h
@@ -79,7 +79,7 @@ namespace winrt::TerminalApp::implementation
         void EnterZoom();
         void ExitZoom();
 
-        std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions(const bool asContent = false) const override;
+        std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions(BuildStartupKind kind) const override;
 
         int GetLeafPaneCount() const noexcept;
 
diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp
index 0ca513a064a..4a37b0a733e 100644
--- a/src/cascadia/TerminalApp/TerminalWindow.cpp
+++ b/src/cascadia/TerminalApp/TerminalWindow.cpp
@@ -267,7 +267,7 @@ namespace winrt::TerminalApp::implementation
     {
         if (_root)
         {
-            _root->CloseWindow(true);
+            _root->PersistState();
         }
     }
 
@@ -916,32 +916,11 @@ namespace winrt::TerminalApp::implementation
     // - <none>
     // Return Value:
     // - <none>
-    void TerminalWindow::CloseWindow(LaunchPosition pos, const bool isLastWindow)
+    void TerminalWindow::CloseWindow()
     {
         if (_root)
         {
-            // If persisted layout is enabled and we are the last window closing
-            // we should save our state.
-            if (_settings.GlobalSettings().ShouldUsePersistedLayout() && isLastWindow)
-            {
-                if (const auto layout = _root->GetWindowLayout())
-                {
-                    layout.InitialPosition(pos);
-                    const auto state = ApplicationState::SharedInstance();
-                    state.PersistedWindowLayouts(winrt::single_threaded_vector<WindowLayout>({ layout }));
-                }
-            }
-
-            _root->CloseWindow(false);
-        }
-    }
-
-    void TerminalWindow::ClearPersistedWindowState()
-    {
-        if (_settings.GlobalSettings().ShouldUsePersistedLayout())
-        {
-            auto state = ApplicationState::SharedInstance();
-            state.PersistedWindowLayouts(nullptr);
+            _root->CloseWindow();
         }
     }
 
@@ -1134,19 +1113,6 @@ namespace winrt::TerminalApp::implementation
         return winrt::to_hstring(_appArgs.GetExitMessage());
     }
 
-    hstring TerminalWindow::GetWindowLayoutJson(LaunchPosition position)
-    {
-        if (_root != nullptr)
-        {
-            if (const auto layout = _root->GetWindowLayout())
-            {
-                layout.InitialPosition(position);
-                return WindowLayout::ToJson(layout);
-            }
-        }
-        return L"";
-    }
-
     void TerminalWindow::SetPersistedLayoutIdx(const uint32_t idx)
     {
         _loadFromPersistedLayoutIdx = idx;
diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h
index 8bf04971afb..d48d8d4f242 100644
--- a/src/cascadia/TerminalApp/TerminalWindow.h
+++ b/src/cascadia/TerminalApp/TerminalWindow.h
@@ -95,8 +95,6 @@ namespace winrt::TerminalApp::implementation
         bool AlwaysOnTop() const;
         bool AutoHideWindow();
 
-        hstring GetWindowLayoutJson(Microsoft::Terminal::Settings::Model::LaunchPosition position);
-
         void IdentifyWindow();
         void RenameFailed();
 
@@ -104,9 +102,6 @@ namespace winrt::TerminalApp::implementation
         winrt::Microsoft::Terminal::Settings::Model::WindowLayout LoadPersistedLayout();
 
         void SetPersistedLayoutIdx(const uint32_t idx);
-        void SetNumberOfOpenWindows(const uint64_t num);
-        bool ShouldUsePersistedLayout() const;
-        void ClearPersistedWindowState();
 
         void RequestExitFullscreen();
 
@@ -125,7 +120,7 @@ namespace winrt::TerminalApp::implementation
         void TitlebarClicked();
         bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
 
-        void CloseWindow(Microsoft::Terminal::Settings::Model::LaunchPosition position, const bool isLastWindow);
+        void CloseWindow();
         void WindowVisibilityChanged(const bool showOrHide);
 
         winrt::TerminalApp::TaskbarState TaskbarState();
@@ -218,7 +213,7 @@ namespace winrt::TerminalApp::implementation
 
         FORWARDED_TYPED_EVENT(SetTitleBarContent, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::UIElement, _root, SetTitleBarContent);
         FORWARDED_TYPED_EVENT(TitleChanged, winrt::Windows::Foundation::IInspectable, winrt::hstring, _root, TitleChanged);
-        FORWARDED_TYPED_EVENT(LastTabClosed, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::LastTabClosedEventArgs, _root, LastTabClosed);
+        FORWARDED_TYPED_EVENT(CloseWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, CloseWindowRequested);
         FORWARDED_TYPED_EVENT(FocusModeChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, FocusModeChanged);
         FORWARDED_TYPED_EVENT(FullscreenChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, FullscreenChanged);
         FORWARDED_TYPED_EVENT(ChangeMaximizeRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, ChangeMaximizeRequested);
diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl
index a1e84211542..1700686e1db 100644
--- a/src/cascadia/TerminalApp/TerminalWindow.idl
+++ b/src/cascadia/TerminalApp/TerminalWindow.idl
@@ -74,7 +74,6 @@ namespace TerminalApp
 
         void IdentifyWindow();
         void SetPersistedLayoutIdx(UInt32 idx);
-        void ClearPersistedWindowState();
 
         void RenameFailed();
         void RequestExitFullscreen();
@@ -89,7 +88,7 @@ namespace TerminalApp
         Boolean GetInitialAlwaysOnTop();
         Single CalcSnappedDimension(Boolean widthOrHeight, Single dimension);
         void TitlebarClicked();
-        void CloseWindow(Microsoft.Terminal.Settings.Model.LaunchPosition position, Boolean isLastWindow);
+        void CloseWindow();
         void WindowVisibilityChanged(Boolean showOrHide);
 
         TaskbarState TaskbarState{ get; };
@@ -97,8 +96,6 @@ namespace TerminalApp
         Windows.UI.Xaml.Media.Brush FrameBrush { get; };
         void WindowActivated(Boolean activated);
 
-        String GetWindowLayoutJson(Microsoft.Terminal.Settings.Model.LaunchPosition position);
-
         Boolean GetMinimizeToNotificationArea();
         Boolean GetAlwaysShowNotificationIcon();
         Boolean GetShowTitleInTitlebar();
@@ -118,7 +115,7 @@ namespace TerminalApp
 
         event Windows.Foundation.TypedEventHandler<Object, Windows.UI.Xaml.UIElement> SetTitleBarContent;
         event Windows.Foundation.TypedEventHandler<Object, String> TitleChanged;
-        event Windows.Foundation.TypedEventHandler<Object, LastTabClosedEventArgs> LastTabClosed;
+        event Windows.Foundation.TypedEventHandler<Object, Object> CloseWindowRequested;
         event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Settings.Model.Theme> RequestedThemeChanged;
         event Windows.Foundation.TypedEventHandler<Object, Object> FocusModeChanged;
         event Windows.Foundation.TypedEventHandler<Object, Object> FullscreenChanged;
diff --git a/src/cascadia/TerminalConnection/TerminalConnection.vcxproj b/src/cascadia/TerminalConnection/TerminalConnection.vcxproj
index f0a2e4e6b49..8eda9937725 100644
--- a/src/cascadia/TerminalConnection/TerminalConnection.vcxproj
+++ b/src/cascadia/TerminalConnection/TerminalConnection.vcxproj
@@ -96,7 +96,6 @@
     </Link>
   </ItemDefinitionGroup>
   <Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
-
   <!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
   <Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
-</Project>
+</Project>
\ No newline at end of file
diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp
index 95c80e4e9ad..4c36320d9b2 100644
--- a/src/cascadia/TerminalControl/ControlCore.cpp
+++ b/src/cascadia/TerminalControl/ControlCore.cpp
@@ -9,8 +9,10 @@
 #include <dsound.h>
 
 #include <DefaultSettings.h>
-#include <utils.hpp>
 #include <LibraryResources.h>
+#include <unicode.hpp>
+#include <utils.hpp>
+#include <WinUser.h>
 
 #include "EventArgs.h"
 #include "../../renderer/atlas/AtlasEngine.h"
@@ -399,10 +401,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
             _initializedTerminal.store(true, std::memory_order_relaxed);
         } // scope for TerminalLock
 
-        // Start the connection outside of lock, because it could
-        // start writing output immediately.
-        _connection.Start();
-
         return true;
     }
 
@@ -1711,6 +1709,50 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         }
     }
 
+    void ControlCore::PersistToPath(const wchar_t* path) const
+    {
+        const auto lock = _terminal->LockForReading();
+        _terminal->SerializeMainBuffer(path);
+    }
+
+    void ControlCore::RestoreFromPath(const wchar_t* path) const
+    {
+        const wil::unique_handle file{ CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, nullptr) };
+        if (!file)
+        {
+            return;
+        }
+
+        wchar_t buffer[32 * 1024];
+        DWORD read = 0;
+
+        // Ensure the text file starts with a UTF-16 BOM.
+        if (!ReadFile(file.get(), &buffer[0], 2, &read, nullptr) || read < 2 || buffer[0] != L'\uFEFF')
+        {
+            return;
+        }
+
+        for (;;)
+        {
+            if (!ReadFile(file.get(), &buffer[0], sizeof(buffer), &read, nullptr))
+            {
+                break;
+            }
+
+            const auto lock = _terminal->LockForWriting();
+            _terminal->Write({ &buffer[0], read / 2 });
+
+            if (read < sizeof(buffer))
+            {
+                break;
+            }
+        }
+
+        // This pushes the restored contents up into the scrollback.
+        const auto lock = _terminal->LockForWriting();
+        _terminal->Write(L"\x1b[2J");
+    }
+
     void ControlCore::_rendererWarning(const HRESULT hr, wil::zwstring_view parameter)
     {
         RendererWarning.raise(*this, winrt::make<RendererWarningArgs>(hr, winrt::hstring{ parameter }));
diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h
index 7dbf228cd26..ee0d68f6a8a 100644
--- a/src/cascadia/TerminalControl/ControlCore.h
+++ b/src/cascadia/TerminalControl/ControlCore.h
@@ -150,6 +150,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         void ColorSelection(const Control::SelectionColor& fg, const Control::SelectionColor& bg, Core::MatchMode matchMode);
 
         void Close();
+        void PersistToPath(const wchar_t* path) const;
+        void RestoreFromPath(const wchar_t* path) const;
 
 #pragma region ICoreState
         const size_t TaskbarState() const noexcept;
diff --git a/src/cascadia/TerminalControl/IControlSettings.idl b/src/cascadia/TerminalControl/IControlSettings.idl
index 2db92a86a83..29cd96b6f8e 100644
--- a/src/cascadia/TerminalControl/IControlSettings.idl
+++ b/src/cascadia/TerminalControl/IControlSettings.idl
@@ -32,7 +32,8 @@ namespace Microsoft.Terminal.Control
         String ProfileName;
         String ProfileSource;
 
-        Boolean EnableUnfocusedAcrylic;
+        Boolean EnableUnfocusedAcrylic { get; };
+        Guid SessionId { get; };
         ScrollbarState ScrollState { get; };
 
         String FontFace { get; };
diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp
index 303d6d61482..0e459e9238b 100644
--- a/src/cascadia/TerminalControl/TermControl.cpp
+++ b/src/cascadia/TerminalControl/TermControl.cpp
@@ -5,6 +5,7 @@
 #include "TermControl.h"
 
 #include <LibraryResources.h>
+#include <utils.hpp>
 
 #include "TermControlAutomationPeer.h"
 #include "../../renderer/atlas/AtlasEngine.h"
@@ -1068,7 +1069,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
             {
                 return false;
             }
+
             _interactivity.Initialize();
+
+            if (!_restorePath.empty())
+            {
+                winrt::get_self<ControlCore>(_core)->RestoreFromPath(_restorePath.c_str());
+                _restorePath = {};
+            }
+
+            _core.Connection().Start();
         }
         else
         {
@@ -2259,6 +2269,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         return _core.ExpandSelectionToWord();
     }
 
+    void TermControl::RestoreFromPath(winrt::hstring path)
+    {
+        _restorePath = std::move(path);
+    }
+
+    void TermControl::PersistToPath(const winrt::hstring& path) const
+    {
+        winrt::get_self<ControlCore>(_core)->PersistToPath(path.c_str());
+    }
+
     void TermControl::Close()
     {
         if (!_IsClosing())
@@ -2293,6 +2313,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
             }
         }
     }
+
     void TermControl::Detach()
     {
         _revokers = {};
diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h
index 2a8b28567ec..a12aa964664 100644
--- a/src/cascadia/TerminalControl/TermControl.h
+++ b/src/cascadia/TerminalControl/TermControl.h
@@ -45,6 +45,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         void ToggleMarkMode();
         bool SwitchSelectionEndpoint();
         bool ExpandSelectionToWord();
+        void RestoreFromPath(winrt::hstring path);
+        void PersistToPath(const winrt::hstring& path) const;
         void Close();
         Windows::Foundation::Size CharacterDimensions() const;
         Windows::Foundation::Size MinimumSize();
@@ -249,6 +251,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         SafeDispatcherTimer _blinkTimer;
 
         winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker;
+        winrt::hstring _restorePath;
         bool _showMarksInScrollbar{ false };
 
         bool _isBackgroundLight{ false };
diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl
index b105748ff64..8a91707c3af 100644
--- a/src/cascadia/TerminalControl/TermControl.idl
+++ b/src/cascadia/TerminalControl/TermControl.idl
@@ -93,6 +93,8 @@ namespace Microsoft.Terminal.Control
         Boolean SwitchSelectionEndpoint();
         Boolean ExpandSelectionToWord();
         void ClearBuffer(ClearBufferType clearType);
+        void RestoreFromPath(String path);
+        void PersistToPath(String path);
         void Close();
         Windows.Foundation.Size CharacterDimensions { get; };
         Windows.Foundation.Size MinimumSize { get; };
diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp
index 6eabcdab072..4a6c1187da5 100644
--- a/src/cascadia/TerminalCore/Terminal.cpp
+++ b/src/cascadia/TerminalCore/Terminal.cpp
@@ -1549,6 +1549,11 @@ std::wstring_view Terminal::CurrentCommand() const
     return _activeBuffer().CurrentCommand();
 }
 
+void Terminal::SerializeMainBuffer(const wchar_t* destination) const
+{
+    _mainBuffer->Serialize(destination);
+}
+
 void Terminal::ColorSelection(const TextAttribute& attr, winrt::Microsoft::Terminal::Core::MatchMode matchMode)
 {
     const auto colorSelection = [this](const til::point coordStart, const til::point coordEnd, const TextAttribute& attr) {
diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp
index 80c5e3cc905..b10c0d893ac 100644
--- a/src/cascadia/TerminalCore/Terminal.hpp
+++ b/src/cascadia/TerminalCore/Terminal.hpp
@@ -126,6 +126,7 @@ class Microsoft::Terminal::Core::Terminal final :
     til::property<bool> AlwaysNotifyOnBufferRotation;
 
     std::wstring_view CurrentCommand() const;
+    void SerializeMainBuffer(const wchar_t* destination) const;
 
 #pragma region ITerminalApi
     // These methods are defined in TerminalApi.cpp
diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp
index a276ebeff2b..598b77a4051 100644
--- a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp
+++ b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp
@@ -129,6 +129,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
         {
             ss << fmt::format(L"--profile \"{}\" ", Profile());
         }
+
+        if (const auto id = SessionId(); id != winrt::guid{})
+        {
+            const auto str = ::Microsoft::Console::Utils::GuidToString(id);
+            ss << fmt::format(L"--sessionId \"{}\" ", str);
+        }
+
         // The caller is always expected to provide the evaluated profile in the
         // NewTerminalArgs, not the index
         //
diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h
index 6d83964cbbc..4062cee426c 100644
--- a/src/cascadia/TerminalSettingsModel/ActionArgs.h
+++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h
@@ -309,6 +309,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
         ACTION_ARG(Windows::Foundation::IReference<Windows::UI::Color>, TabColor, nullptr);
         ACTION_ARG(Windows::Foundation::IReference<int32_t>, ProfileIndex, nullptr);
         ACTION_ARG(winrt::hstring, Profile, L"");
+        ACTION_ARG(winrt::guid, SessionId, winrt::guid{});
         ACTION_ARG(bool, AppendCommandLine, false);
         ACTION_ARG(Windows::Foundation::IReference<bool>, SuppressApplicationTitle, nullptr);
         ACTION_ARG(winrt::hstring, ColorScheme);
@@ -322,6 +323,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
         static constexpr std::string_view TabColorKey{ "tabColor" };
         static constexpr std::string_view ProfileIndexKey{ "index" };
         static constexpr std::string_view ProfileKey{ "profile" };
+        static constexpr std::string_view SessionIdKey{ "sessionId" };
         static constexpr std::string_view AppendCommandLineKey{ "appendCommandLine" };
         static constexpr std::string_view SuppressApplicationTitleKey{ "suppressApplicationTitle" };
         static constexpr std::string_view ColorSchemeKey{ "colorScheme" };
@@ -362,6 +364,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
             JsonUtils::GetValueForKey(json, TabTitleKey, args->_TabTitle);
             JsonUtils::GetValueForKey(json, ProfileIndexKey, args->_ProfileIndex);
             JsonUtils::GetValueForKey(json, ProfileKey, args->_Profile);
+            JsonUtils::GetValueForKey(json, SessionIdKey, args->_SessionId);
             JsonUtils::GetValueForKey(json, TabColorKey, args->_TabColor);
             JsonUtils::GetValueForKey(json, SuppressApplicationTitleKey, args->_SuppressApplicationTitle);
             JsonUtils::GetValueForKey(json, ColorSchemeKey, args->_ColorScheme);
@@ -383,6 +386,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
             JsonUtils::SetValueForKey(json, TabTitleKey, args->_TabTitle);
             JsonUtils::SetValueForKey(json, ProfileIndexKey, args->_ProfileIndex);
             JsonUtils::SetValueForKey(json, ProfileKey, args->_Profile);
+            JsonUtils::SetValueForKey(json, SessionIdKey, args->_SessionId);
             JsonUtils::SetValueForKey(json, TabColorKey, args->_TabColor);
             JsonUtils::SetValueForKey(json, SuppressApplicationTitleKey, args->_SuppressApplicationTitle);
             JsonUtils::SetValueForKey(json, ColorSchemeKey, args->_ColorScheme);
@@ -400,6 +404,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
             copy->_TabColor = _TabColor;
             copy->_ProfileIndex = _ProfileIndex;
             copy->_Profile = _Profile;
+            copy->_SessionId = _SessionId;
             copy->_SuppressApplicationTitle = _SuppressApplicationTitle;
             copy->_ColorScheme = _ColorScheme;
             copy->_Elevate = _Elevate;
diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl
index 32f9005fdee..f8e6f4eb9cd 100644
--- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl
+++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl
@@ -132,6 +132,7 @@ namespace Microsoft.Terminal.Settings.Model
         String TabTitle;
         Windows.Foundation.IReference<Windows.UI.Color> TabColor;
         String Profile; // Either a GUID or a profile's name if the GUID isn't a match
+        Guid SessionId;
         Boolean AppendCommandLine;
 
         // We use IReference<> to treat some args as nullable where null means
diff --git a/src/cascadia/TerminalSettingsModel/ApplicationState.cpp b/src/cascadia/TerminalSettingsModel/ApplicationState.cpp
index 347a180b708..30b44a55b21 100644
--- a/src/cascadia/TerminalSettingsModel/ApplicationState.cpp
+++ b/src/cascadia/TerminalSettingsModel/ApplicationState.cpp
@@ -240,8 +240,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
     // Returns the application-global ApplicationState object.
     Microsoft::Terminal::Settings::Model::ApplicationState ApplicationState::SharedInstance()
     {
-        auto root{ GetBaseSettingsPath() };
-        static auto state = winrt::make_self<ApplicationState>(root);
+        static auto state = winrt::make_self<ApplicationState>(GetBaseSettingsPath());
         return *state;
     }
 
@@ -294,6 +293,20 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
         return root;
     }
 
+    void ApplicationState::AppendPersistedWindowLayout(Model::WindowLayout layout)
+    {
+        {
+            const auto state = _state.lock();
+            if (!state->PersistedWindowLayouts || !*state->PersistedWindowLayouts)
+            {
+                state->PersistedWindowLayouts = winrt::single_threaded_vector<Model::WindowLayout>();
+            }
+            state->PersistedWindowLayouts->Append(std::move(layout));
+        }
+
+        _throttler();
+    }
+
     // Generate all getter/setters
 #define MTSM_APPLICATION_STATE_GEN(source, type, name, key, ...) \
     type ApplicationState::name() const noexcept                 \
diff --git a/src/cascadia/TerminalSettingsModel/ApplicationState.h b/src/cascadia/TerminalSettingsModel/ApplicationState.h
index bacffdba2f7..df7fd803e70 100644
--- a/src/cascadia/TerminalSettingsModel/ApplicationState.h
+++ b/src/cascadia/TerminalSettingsModel/ApplicationState.h
@@ -69,6 +69,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
         void FromJson(const Json::Value& root, FileSource parseSource) const noexcept;
         Json::Value ToJson(FileSource parseSource) const noexcept;
 
+        void AppendPersistedWindowLayout(Model::WindowLayout layout);
+
         // State getters/setters
 #define MTSM_APPLICATION_STATE_GEN(source, type, name, key, ...) \
     type name() const noexcept;                                  \
diff --git a/src/cascadia/TerminalSettingsModel/ApplicationState.idl b/src/cascadia/TerminalSettingsModel/ApplicationState.idl
index e48c1e7f86b..e35447773a6 100644
--- a/src/cascadia/TerminalSettingsModel/ApplicationState.idl
+++ b/src/cascadia/TerminalSettingsModel/ApplicationState.idl
@@ -32,6 +32,8 @@ namespace Microsoft.Terminal.Settings.Model
         void Flush();
         void Reset();
 
+        void AppendPersistedWindowLayout(WindowLayout layout);
+
         String SettingsHash;
         Windows.Foundation.Collections.IVector<WindowLayout> PersistedWindowLayouts;
         Windows.Foundation.Collections.IVector<String> RecentCommands;
diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h
index 03f3ea071ec..03553c8b73d 100644
--- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h
+++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h
@@ -106,6 +106,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
         static Model::CascadiaSettings LoadDefaults();
         static Model::CascadiaSettings LoadAll();
 
+        static winrt::hstring SettingsDirectory();
         static winrt::hstring SettingsPath();
         static winrt::hstring DefaultSettingsPath();
         static winrt::hstring ApplicationDisplayName();
diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl b/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl
index 1889871bb3e..165a7fed9fa 100644
--- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl
+++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl
@@ -12,6 +12,7 @@ namespace Microsoft.Terminal.Settings.Model
         static CascadiaSettings LoadDefaults();
         static CascadiaSettings LoadAll();
 
+        static String SettingsDirectory { get; };
         static String SettingsPath { get; };
         static String DefaultSettingsPath { get; };
         static Boolean IsPortableMode { get; };
diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp
index 244c200dad8..93af43319bc 100644
--- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp
+++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp
@@ -1244,6 +1244,13 @@ winrt::hstring CascadiaSettings::_calculateHash(std::string_view settings, const
     return winrt::hstring{ hash };
 }
 
+// This returns something akin to %LOCALAPPDATA%\Packages\WindowsTerminalDev_8wekyb3d8bbwe\LocalState
+// just like SettingsPath(), but without the trailing \settings.json.
+winrt::hstring CascadiaSettings::SettingsDirectory()
+{
+    return winrt::hstring{ GetBaseSettingsPath().native() };
+}
+
 // function Description:
 // - Returns the full path to the settings file, either within the application
 //   package, or in its unpackaged location. This path is under the "Local
diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp
index 6f1d2932699..29b9bb811fe 100644
--- a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp
+++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp
@@ -126,6 +126,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
 
         if (newTerminalArgs)
         {
+            if (const auto id = newTerminalArgs.SessionId(); id != winrt::guid{})
+            {
+                defaultSettings.SessionId(id);
+            }
+
             // Override commandline, starting directory if they exist in newTerminalArgs
             if (!newTerminalArgs.Commandline().empty())
             {
diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.h b/src/cascadia/TerminalSettingsModel/TerminalSettings.h
index d3410ba2628..57ec8659fd6 100644
--- a/src/cascadia/TerminalSettingsModel/TerminalSettings.h
+++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.h
@@ -118,6 +118,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
         INHERITABLE_SETTING(Model::TerminalSettings, hstring, ProfileName);
         INHERITABLE_SETTING(Model::TerminalSettings, hstring, ProfileSource);
 
+        INHERITABLE_SETTING(Model::TerminalSettings, guid, SessionId);
         INHERITABLE_SETTING(Model::TerminalSettings, bool, EnableUnfocusedAcrylic, false);
         INHERITABLE_SETTING(Model::TerminalSettings, bool, UseAcrylic, false);
         INHERITABLE_SETTING(Model::TerminalSettings, double, Opacity, UseAcrylic() ? 0.5 : 1.0);
diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.idl b/src/cascadia/TerminalSettingsModel/TerminalSettings.idl
index 23c305ee4e0..32bf6111909 100644
--- a/src/cascadia/TerminalSettingsModel/TerminalSettings.idl
+++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.idl
@@ -41,6 +41,7 @@ namespace Microsoft.Terminal.Settings.Model
         // The getters for these are already defined in IControlSettings. So
         // we're just adding the setters here, because TerminalApp likes to be
         // able to change these at runtime (e.g. when duplicating a pane).
+        Guid SessionId { set; };
         String Commandline { set; };
         String StartingDirectory { set; };
 
diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp
index 8be39b44ca6..8fc12f493db 100644
--- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp
+++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp
@@ -82,7 +82,6 @@ namespace RemotingUnitTests
         void Summon(const Remoting::SummonWindowBehavior& /*args*/) DIE;
         void RequestShowNotificationIcon() DIE;
         void RequestHideNotificationIcon() DIE;
-        winrt::hstring GetWindowLayout() DIE;
         void RequestQuitAll() DIE;
         void Quit() DIE;
         void AttachContentToWindow(Remoting::AttachRequest) DIE;
@@ -97,7 +96,6 @@ namespace RemotingUnitTests
         til::typed_event<> HideNotificationIconRequested;
         til::typed_event<> QuitAllRequested;
         til::typed_event<> QuitRequested;
-        til::typed_event<winrt::Windows::Foundation::IInspectable, Remoting::GetWindowLayoutArgs> GetWindowLayoutRequested;
         til::typed_event<winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::AttachRequest> AttachRequested;
         til::typed_event<winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs> SendContentRequested;
     };
@@ -117,7 +115,6 @@ namespace RemotingUnitTests
         void SummonAllWindows() DIE;
         bool DoesQuakeWindowExist() DIE;
         winrt::Windows::Foundation::Collections::IVectorView<Remoting::PeasantInfo> GetPeasantInfos() DIE;
-        winrt::Windows::Foundation::Collections::IVector<winrt::hstring> GetAllWindowLayouts() DIE;
         void RequestMoveContent(winrt::hstring, winrt::hstring, uint32_t, winrt::Windows::Foundation::IReference<winrt::Windows::Foundation::Rect>) DIE;
         void RequestSendContent(Remoting::RequestReceiveContentArgs) DIE;
 
@@ -126,7 +123,6 @@ namespace RemotingUnitTests
         til::typed_event<> HideNotificationIconRequested;
         til::typed_event<> WindowCreated;
         til::typed_event<> WindowClosed;
-        til::typed_event<winrt::Windows::Foundation::IInspectable, Remoting::QuitAllRequestedArgs> QuitAllRequested;
         til::typed_event<winrt::Windows::Foundation::IInspectable, Remoting::WindowRequestedArgs> RequestNewWindow;
     };
 
diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp
index 816e4cda6e4..0fe39c1cc56 100644
--- a/src/cascadia/WindowsTerminal/AppHost.cpp
+++ b/src/cascadia/WindowsTerminal/AppHost.cpp
@@ -215,49 +215,51 @@ void AppHost::_HandleSessionRestore(const bool startedForContent)
     // we'll leave it here.
     const auto numPeasants = _windowManager.GetNumberOfPeasants();
     // Don't attempt to session restore if we're just making a window for tear-out
-    if (!startedForContent && numPeasants == 1)
+    if (startedForContent || numPeasants != 1 || !_appLogic.ShouldUsePersistedLayout())
     {
-        const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts();
-        if (_appLogic.ShouldUsePersistedLayout() &&
-            layouts &&
-            layouts.Size() > 0)
+        return;
+    }
+
+    const auto state = ApplicationState::SharedInstance();
+    const auto layouts = state.PersistedWindowLayouts();
+
+    if (layouts && layouts.Size() > 0)
+    {
+        uint32_t startIdx = 0;
+        // We want to create a window for every saved layout.
+        // If we are the only window, and no commandline arguments were provided
+        // then we should just use the current window to load the first layout.
+        // Otherwise create this window normally with its commandline, and create
+        // a new window using the first saved layout information.
+        // The 2nd+ layout will always get a new window.
+        if (!_windowLogic.HasCommandlineArguments() &&
+            !_appLogic.HasSettingsStartupActions())
         {
-            uint32_t startIdx = 0;
-            // We want to create a window for every saved layout.
-            // If we are the only window, and no commandline arguments were provided
-            // then we should just use the current window to load the first layout.
-            // Otherwise create this window normally with its commandline, and create
-            // a new window using the first saved layout information.
-            // The 2nd+ layout will always get a new window.
-            if (!_windowLogic.HasCommandlineArguments() &&
-                !_appLogic.HasSettingsStartupActions())
-            {
-                _windowLogic.SetPersistedLayoutIdx(startIdx);
-                startIdx += 1;
-            }
+            _windowLogic.SetPersistedLayoutIdx(startIdx);
+            startIdx += 1;
+        }
 
-            // Create new windows for each of the other saved layouts.
-            for (const auto size = layouts.Size(); startIdx < size; startIdx += 1)
-            {
-                auto newWindowArgs = fmt::format(L"{0} -w new -s {1}", args.Commandline()[0], startIdx);
-
-                STARTUPINFO si;
-                memset(&si, 0, sizeof(si));
-                si.cb = sizeof(si);
-                wil::unique_process_information pi;
-
-                LOG_IF_WIN32_BOOL_FALSE(CreateProcessW(nullptr,
-                                                       newWindowArgs.data(),
-                                                       nullptr, // lpProcessAttributes
-                                                       nullptr, // lpThreadAttributes
-                                                       false, // bInheritHandles
-                                                       DETACHED_PROCESS | CREATE_UNICODE_ENVIRONMENT, // doCreationFlags
-                                                       nullptr, // lpEnvironment
-                                                       nullptr, // lpStartingDirectory
-                                                       &si, // lpStartupInfo
-                                                       &pi // lpProcessInformation
-                                                       ));
-            }
+        // Create new windows for each of the other saved layouts.
+        for (const auto size = layouts.Size(); startIdx < size; startIdx += 1)
+        {
+            auto newWindowArgs = fmt::format(L"{0} -w new -s {1}", args.Commandline()[0], startIdx);
+
+            STARTUPINFO si;
+            memset(&si, 0, sizeof(si));
+            si.cb = sizeof(si);
+            wil::unique_process_information pi;
+
+            LOG_IF_WIN32_BOOL_FALSE(CreateProcessW(nullptr,
+                                                   newWindowArgs.data(),
+                                                   nullptr, // lpProcessAttributes
+                                                   nullptr, // lpThreadAttributes
+                                                   false, // bInheritHandles
+                                                   DETACHED_PROCESS | CREATE_UNICODE_ENVIRONMENT, // doCreationFlags
+                                                   nullptr, // lpEnvironment
+                                                   nullptr, // lpStartingDirectory
+                                                   &si, // lpStartupInfo
+                                                   &pi // lpProcessInformation
+                                                   ));
         }
     }
 }
@@ -317,7 +319,7 @@ void AppHost::Initialize()
     // Register the 'X' button of the window for a warning experience of multiple
     // tabs opened, this is consistent with Alt+F4 closing
     _windowCallbacks.WindowCloseButtonClicked = _window->WindowCloseButtonClicked([this]() {
-        _CloseRequested(nullptr, nullptr);
+        _windowLogic.CloseWindow();
     });
     // If the user requests a close in another way handle the same as if the 'X'
     // was clicked.
@@ -350,7 +352,7 @@ void AppHost::Initialize()
     _windowCallbacks.AutomaticShutdownRequested = _window->AutomaticShutdownRequested([this]() {
         // Raised when the OS is beginning an update of the app. We will quit,
         // to save our state, before the OS manually kills us.
-        Remoting::WindowManager::RequestQuitAll(_peasant);
+        _quit();
     });
 
     // Load bearing: make sure the PropertyChanged handler is added before we
@@ -362,7 +364,7 @@ void AppHost::Initialize()
     _windowLogic.Create();
 
     _revokers.TitleChanged = _windowLogic.TitleChanged(winrt::auto_revoke, { this, &AppHost::AppTitleChanged });
-    _revokers.LastTabClosed = _windowLogic.LastTabClosed(winrt::auto_revoke, { this, &AppHost::LastTabClosed });
+    _revokers.CloseWindowRequested = _windowLogic.CloseWindowRequested(winrt::auto_revoke, { this, &AppHost::_CloseRequested });
     _revokers.SetTaskbarProgress = _windowLogic.SetTaskbarProgress(winrt::auto_revoke, { this, &AppHost::SetTaskbarProgress });
     _revokers.IdentifyWindowsRequested = _windowLogic.IdentifyWindowsRequested(winrt::auto_revoke, { this, &AppHost::_IdentifyWindowsRequested });
     _revokers.RenameWindowRequested = _windowLogic.RenameWindowRequested(winrt::auto_revoke, { this, &AppHost::_RenameWindowRequested });
@@ -382,22 +384,6 @@ void AppHost::Initialize()
     _revokers.RequestReceiveContent = _windowLogic.RequestReceiveContent(winrt::auto_revoke, { this, &AppHost::_handleReceiveContent });
     _revokers.SendContentRequested = _peasant.SendContentRequested(winrt::auto_revoke, { this, &AppHost::_handleSendContent });
 
-    // Add our GetWindowLayoutRequested handler AFTER the xaml island is
-    // started. Our _GetWindowLayoutAsync handler requires us to be able to work
-    // on our UI thread, which requires that we have a Dispatcher ready for us
-    // to move to. If we set up this callback in the ctor, then it is possible
-    // for there to be a time slice where
-    // * the monarch creates the peasant for us,
-    // * we get constructed (registering the callback)
-    // * then the monarch attempts to query all _peasants_ for their layout,
-    //   coming back to ask us even before XAML has been created.
-    _GetWindowLayoutRequestedToken = _peasant.GetWindowLayoutRequested([this](auto&&,
-                                                                              const Remoting::GetWindowLayoutArgs& args) {
-        // The peasants are running on separate threads, so they'll need to
-        // swap what context they are in to the ui thread to get the actual layout.
-        args.WindowLayoutJsonAsync(_GetWindowLayoutAsync());
-    });
-
     // BODGY
     // On certain builds of Windows, when Terminal is set as the default
     // it will accumulate an unbounded amount of queued animations while
@@ -452,6 +438,16 @@ void AppHost::Close()
     }
 }
 
+winrt::fire_and_forget AppHost::_quit()
+{
+    const auto peasant = _peasant;
+
+    co_await winrt::resume_background();
+
+    ApplicationState::SharedInstance().PersistedWindowLayouts(nullptr);
+    peasant.RequestQuitAll();
+}
+
 void AppHost::_revokeWindowCallbacks()
 {
     // You'll recall, IslandWindow isn't a WinRT type so it can't have auto-revokers.
@@ -514,42 +510,6 @@ void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /*
     _windowManager.UpdateActiveTabTitle(newTitle, _peasant);
 }
 
-// Method Description:
-// - Called when no tab is remaining to close the window.
-// Arguments:
-// - sender: unused
-// - LastTabClosedEventArgs: unused
-// Return Value:
-// - <none>
-void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::TerminalApp::LastTabClosedEventArgs& args)
-{
-    // We don't want to try to save layouts if we are about to close.
-    _peasant.GetWindowLayoutRequested(_GetWindowLayoutRequestedToken);
-
-    // If the user closes the last tab, in the last window, _by closing the tab_
-    // (not by closing the whole window), we need to manually persist an empty
-    // window state here. That will cause the terminal to re-open with the usual
-    // settings (not the persisted state)
-    if (args.ClearPersistedState() &&
-        _windowManager.GetNumberOfPeasants() == 1)
-    {
-        _windowLogic.ClearPersistedWindowState();
-    }
-    // Remove ourself from the list of peasants so that we aren't included in
-    // any future requests. This will also mean we block until any existing
-    // event handler finishes.
-    _windowManager.SignalClose(_peasant);
-
-    if (Utils::IsWindows11())
-    {
-        PostQuitMessage(0);
-    }
-    else
-    {
-        PostMessageW(_window->GetInteropHandle(), WM_REFRIGERATE, 0, 0);
-    }
-}
-
 LaunchPosition AppHost::_GetWindowLaunchPosition()
 {
     LaunchPosition pos{};
@@ -952,42 +912,6 @@ winrt::fire_and_forget AppHost::_peasantNotifyActivateWindow()
     });
 }
 
-// Method Description:
-// - Asynchronously get the window layout from the current page. This is
-//   done async because we need to switch between the ui thread and the calling
-//   thread.
-// - NB: The peasant calling this must not be running on the UI thread, otherwise
-//   they will crash since they just call .get on the async operation.
-// Arguments:
-// - <none>
-// Return Value:
-// - The window layout as a json string.
-winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> AppHost::_GetWindowLayoutAsync()
-{
-    winrt::hstring layoutJson = L"";
-
-    auto weakThis{ weak_from_this() };
-
-    // Use the main thread since we are accessing controls.
-    co_await wil::resume_foreground(_windowLogic.GetRoot().Dispatcher());
-
-    const auto strongThis = weakThis.lock();
-    // GH #16235: If we don't have a window logic, we're already refrigerating, and won't have our _window either.
-    if (!strongThis || _windowLogic == nullptr)
-    {
-        co_return layoutJson;
-    }
-
-    try
-    {
-        const auto pos = _GetWindowLaunchPosition();
-        layoutJson = _windowLogic.GetWindowLayoutJson(pos);
-    }
-    CATCH_LOG()
-
-    co_return layoutJson;
-}
-
 void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*sender*/,
                             const Remoting::SummonWindowBehavior& args)
 {
@@ -1264,13 +1188,14 @@ winrt::fire_and_forget AppHost::_QuitRequested(const winrt::Windows::Foundation:
     }
 
     _windowLogic.Quit();
+    PostQuitMessage(0);
 }
 
 // Raised from TerminalWindow. We handle by bubbling the request to the window manager.
 void AppHost::_RequestQuitAll(const winrt::Windows::Foundation::IInspectable&,
                               const winrt::Windows::Foundation::IInspectable&)
 {
-    Remoting::WindowManager::RequestQuitAll(_peasant);
+    _quit();
 }
 
 void AppHost::_ShowWindowChanged(const winrt::Windows::Foundation::IInspectable&,
@@ -1370,9 +1295,25 @@ void AppHost::_WindowMoved()
 void AppHost::_CloseRequested(const winrt::Windows::Foundation::IInspectable& /*sender*/,
                               const winrt::Windows::Foundation::IInspectable& /*args*/)
 {
-    const auto pos = _GetWindowLaunchPosition();
-    const bool isLastWindow = _windowManager.GetNumberOfPeasants() == 1;
-    _windowLogic.CloseWindow(pos, isLastWindow);
+    if (_windowManager.GetNumberOfPeasants() <= 1)
+    {
+        _quit();
+        return;
+    }
+
+    // Remove ourself from the list of peasants so that we aren't included in
+    // any future requests. This will also mean we block until any existing
+    // event handler finishes.
+    _windowManager.SignalClose(_peasant);
+
+    if (Utils::IsWindows11())
+    {
+        PostQuitMessage(0);
+    }
+    else
+    {
+        PostMessageW(_window->GetInteropHandle(), WM_REFRIGERATE, 0, 0);
+    }
 }
 
 void AppHost::_PropertyChangedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h
index 25cf599af74..a22fc85a948 100644
--- a/src/cascadia/WindowsTerminal/AppHost.h
+++ b/src/cascadia/WindowsTerminal/AppHost.h
@@ -20,7 +20,6 @@ class AppHost : public std::enable_shared_from_this<AppHost>
             std::unique_ptr<IslandWindow> window = nullptr) noexcept;
 
     void AppTitleChanged(const winrt::Windows::Foundation::IInspectable& sender, winrt::hstring newTitle);
-    void LastTabClosed(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::LastTabClosedEventArgs& args);
     void Initialize();
     void Close();
 
@@ -64,8 +63,7 @@ class AppHost : public std::enable_shared_from_this<AppHost>
 
     uint32_t _launchShowWindowCommand{ SW_NORMAL };
 
-    void _preInit();
-
+    winrt::fire_and_forget _quit();
     void _revokeWindowCallbacks();
 
     void _HandleCommandlineArgs(const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args);
@@ -100,8 +98,6 @@ class AppHost : public std::enable_shared_from_this<AppHost>
     void _DispatchCommandline(winrt::Windows::Foundation::IInspectable sender,
                               winrt::Microsoft::Terminal::Remoting::CommandlineArgs args);
 
-    winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> _GetWindowLayoutAsync();
-
     void _HandleSummon(const winrt::Windows::Foundation::IInspectable& sender,
                        const winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior& args);
 
@@ -112,10 +108,6 @@ class AppHost : public std::enable_shared_from_this<AppHost>
     winrt::fire_and_forget _RenameWindowRequested(const winrt::Windows::Foundation::IInspectable sender,
                                                   const winrt::TerminalApp::RenameWindowRequestedArgs args);
 
-    GUID _CurrentDesktopGuid();
-
-    bool _LazyLoadDesktopManager();
-
     void _HandleSettingsChanged(const winrt::Windows::Foundation::IInspectable& sender,
                                 const winrt::TerminalApp::SettingsLoadEventArgs& args);
 
@@ -168,8 +160,6 @@ class AppHost : public std::enable_shared_from_this<AppHost>
     void _stopFrameTimer();
     void _updateFrameColor(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&);
 
-    winrt::event_token _GetWindowLayoutRequestedToken;
-
     // Helper struct. By putting these all into one struct, we can revoke them
     // all at once, by assigning _revokers to a fresh Revokers instance. That'll
     // cause us to dtor the old one, which will immediately call revoke on all
@@ -194,7 +184,7 @@ class AppHost : public std::enable_shared_from_this<AppHost>
         winrt::TerminalApp::TerminalWindow::SystemMenuChangeRequested_revoker SystemMenuChangeRequested;
         winrt::TerminalApp::TerminalWindow::ChangeMaximizeRequested_revoker ChangeMaximizeRequested;
         winrt::TerminalApp::TerminalWindow::TitleChanged_revoker TitleChanged;
-        winrt::TerminalApp::TerminalWindow::LastTabClosed_revoker LastTabClosed;
+        winrt::TerminalApp::TerminalWindow::CloseWindowRequested_revoker CloseWindowRequested;
         winrt::TerminalApp::TerminalWindow::SetTaskbarProgress_revoker SetTaskbarProgress;
         winrt::TerminalApp::TerminalWindow::IdentifyWindowsRequested_revoker IdentifyWindowsRequested;
         winrt::TerminalApp::TerminalWindow::RenameWindowRequested_revoker RenameWindowRequested;
@@ -208,7 +198,6 @@ class AppHost : public std::enable_shared_from_this<AppHost>
         winrt::TerminalApp::TerminalWindow::PropertyChanged_revoker PropertyChanged;
         winrt::TerminalApp::TerminalWindow::SettingsChanged_revoker SettingsChanged;
 
-        winrt::Microsoft::Terminal::Remoting::WindowManager::QuitAllRequested_revoker QuitAllRequested;
         winrt::Microsoft::Terminal::Remoting::Peasant::SendContentRequested_revoker SendContentRequested;
     } _revokers{};
 
diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp
index 36d49e2c09f..0fc28a20433 100644
--- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp
+++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp
@@ -348,116 +348,17 @@ void WindowEmperor::_becomeMonarch()
 
     _revokers.WindowCreated = _manager.WindowCreated(winrt::auto_revoke, { this, &WindowEmperor::_numberOfWindowsChanged });
     _revokers.WindowClosed = _manager.WindowClosed(winrt::auto_revoke, { this, &WindowEmperor::_numberOfWindowsChanged });
-
-    // If the monarch receives a QuitAll event it will signal this event to be
-    // ran before each peasant is closed.
-    _revokers.QuitAllRequested = _manager.QuitAllRequested(winrt::auto_revoke, { this, &WindowEmperor::_quitAllRequested });
-
-    // The monarch should be monitoring if it should save the window layout.
-    // We want at least some delay to prevent the first save from overwriting
-    _getWindowLayoutThrottler.emplace(std::chrono::seconds(10), [this]() { _saveWindowLayoutsRepeat(); });
-    _getWindowLayoutThrottler.value()();
 }
 
 // sender and args are always nullptr
 void WindowEmperor::_numberOfWindowsChanged(const winrt::Windows::Foundation::IInspectable&,
                                             const winrt::Windows::Foundation::IInspectable&)
 {
-    if (_getWindowLayoutThrottler)
-    {
-        _getWindowLayoutThrottler.value()();
-    }
-
     // If we closed out the quake window, and don't otherwise need the tray
     // icon, let's get rid of it.
     _checkWindowsForNotificationIcon();
 }
 
-// Raised from our windowManager (on behalf of the monarch). We respond by
-// giving the monarch an async function that the manager should wait on before
-// completing the quit.
-void WindowEmperor::_quitAllRequested(const winrt::Windows::Foundation::IInspectable&,
-                                      const winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs& args)
-{
-    _quitting = true;
-
-    // Make sure that the current timer is destroyed so that it doesn't attempt
-    // to run while we are in the middle of quitting.
-    if (_getWindowLayoutThrottler.has_value())
-    {
-        _getWindowLayoutThrottler.reset();
-    }
-
-    // Tell the monarch to wait for the window layouts to save before
-    // everyone quits.
-    args.BeforeQuitAllAction(_saveWindowLayouts());
-}
-
-#pragma region LayoutPersistence
-
-winrt::Windows::Foundation::IAsyncAction WindowEmperor::_saveWindowLayouts()
-{
-    // Make sure we run on a background thread to not block anything.
-    co_await winrt::resume_background();
-
-    if (_app.Logic().ShouldUsePersistedLayout())
-    {
-        try
-        {
-            TraceLoggingWrite(g_hWindowsTerminalProvider,
-                              "AppHost_SaveWindowLayouts_Collect",
-                              TraceLoggingDescription("Logged when collecting window state"),
-                              TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
-                              TraceLoggingKeyword(TIL_KEYWORD_TRACE));
-
-            const auto layoutJsons = _manager.GetAllWindowLayouts();
-
-            TraceLoggingWrite(g_hWindowsTerminalProvider,
-                              "AppHost_SaveWindowLayouts_Save",
-                              TraceLoggingDescription("Logged when writing window state"),
-                              TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
-                              TraceLoggingKeyword(TIL_KEYWORD_TRACE));
-
-            _app.Logic().SaveWindowLayoutJsons(layoutJsons);
-        }
-        catch (...)
-        {
-            LOG_CAUGHT_EXCEPTION();
-            TraceLoggingWrite(g_hWindowsTerminalProvider,
-                              "AppHost_SaveWindowLayouts_Failed",
-                              TraceLoggingDescription("An error occurred when collecting or writing window state"),
-                              TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
-                              TraceLoggingKeyword(TIL_KEYWORD_TRACE));
-        }
-    }
-
-    co_return;
-}
-
-winrt::fire_and_forget WindowEmperor::_saveWindowLayoutsRepeat()
-{
-    // Make sure we run on a background thread to not block anything.
-    co_await winrt::resume_background();
-
-    co_await _saveWindowLayouts();
-
-    // Don't need to save too frequently.
-    co_await winrt::resume_after(30s);
-
-    // As long as we are supposed to keep saving, request another save.
-    // This will be delayed by the throttler so that at most one save happens
-    // per 10 seconds, if a save is requested by another source simultaneously.
-    if (_getWindowLayoutThrottler.has_value())
-    {
-        TraceLoggingWrite(g_hWindowsTerminalProvider,
-                          "AppHost_requestGetLayout",
-                          TraceLoggingDescription("Logged when triggering a throttled write of the window state"),
-                          TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
-                          TraceLoggingKeyword(TIL_KEYWORD_TRACE));
-
-        _getWindowLayoutThrottler.value()();
-    }
-}
 #pragma endregion
 
 #pragma region WindowProc
@@ -584,6 +485,80 @@ void WindowEmperor::_finalizeSessionPersistence() const
 
     // Ensure to write the state.json before we TerminateProcess()
     state.Flush();
+
+    if (!_app.Logic().ShouldUsePersistedLayout())
+    {
+        return;
+    }
+
+    // Get the "buffer_{guid}.txt" files that we expect to be there
+    std::unordered_set<winrt::guid> sessionIds;
+    if (const auto layouts = state.PersistedWindowLayouts())
+    {
+        for (const auto& windowLayout : layouts)
+        {
+            for (const auto& actionAndArgs : windowLayout.TabLayout())
+            {
+                const auto args = actionAndArgs.Args();
+                NewTerminalArgs terminalArgs{ nullptr };
+
+                if (const auto tabArgs = args.try_as<NewTabArgs>())
+                {
+                    terminalArgs = tabArgs.TerminalArgs();
+                }
+                else if (const auto paneArgs = args.try_as<SplitPaneArgs>())
+                {
+                    terminalArgs = paneArgs.TerminalArgs();
+                }
+
+                if (terminalArgs)
+                {
+                    sessionIds.emplace(terminalArgs.SessionId());
+                }
+            }
+        }
+    }
+
+    // Remove the "buffer_{guid}.txt" files that shouldn't be there
+    // e.g. "buffer_FD40D746-163E-444C-B9B2-6A3EA2B26722.txt"
+    {
+        const std::filesystem::path settingsDirectory{ std::wstring_view{ CascadiaSettings::SettingsDirectory() } };
+        const auto filter = settingsDirectory / L"buffer_*";
+        WIN32_FIND_DATAW ffd;
+
+        // This could also use std::filesystem::directory_iterator.
+        // I was just slightly bothered by how it doesn't have a O(1) .filename()
+        // function, even though the underlying Win32 APIs provide it for free.
+        // Both work fine.
+        const wil::unique_hfind handle{ FindFirstFileExW(filter.c_str(), FindExInfoBasic, &ffd, FindExSearchNameMatch, nullptr, FIND_FIRST_EX_LARGE_FETCH) };
+        if (!handle)
+        {
+            return;
+        }
+
+        do
+        {
+            const auto nameLen = wcsnlen_s(&ffd.cFileName[0], ARRAYSIZE(ffd.cFileName));
+            const std::wstring_view name{ &ffd.cFileName[0], nameLen };
+
+            if (nameLen != 47)
+            {
+                continue;
+            }
+
+            wchar_t guidStr[39];
+            guidStr[0] = L'{';
+            memcpy(&guidStr[1], name.data() + 7, 36 * sizeof(wchar_t));
+            guidStr[37] = L'}';
+            guidStr[38] = L'\0';
+
+            const auto id = Utils::GuidFromString(&guidStr[0]);
+            if (!sessionIds.contains(id))
+            {
+                std::filesystem::remove(settingsDirectory / name);
+            }
+        } while (FindNextFileW(handle.get(), &ffd));
+    }
 }
 
 #pragma endregion
diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h
index 9f44a276c3c..a35032e6d5e 100644
--- a/src/cascadia/WindowsTerminal/WindowEmperor.h
+++ b/src/cascadia/WindowsTerminal/WindowEmperor.h
@@ -44,8 +44,6 @@ class WindowEmperor : public std::enable_shared_from_this<WindowEmperor>
 
     til::shared_mutex<std::vector<std::shared_ptr<WindowThread>>> _oldThreads;
 
-    std::optional<til::throttled_func_trailing<>> _getWindowLayoutThrottler;
-
     winrt::event_token _WindowCreatedToken;
     winrt::event_token _WindowClosedToken;
 
@@ -61,15 +59,10 @@ class WindowEmperor : public std::enable_shared_from_this<WindowEmperor>
 
     void _becomeMonarch();
     void _numberOfWindowsChanged(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&);
-    void _quitAllRequested(const winrt::Windows::Foundation::IInspectable&,
-                           const winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs&);
 
     winrt::fire_and_forget _windowIsQuakeWindowChanged(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::Foundation::IInspectable args);
     winrt::fire_and_forget _windowRequestUpdateSettings();
 
-    winrt::Windows::Foundation::IAsyncAction _saveWindowLayouts();
-    winrt::fire_and_forget _saveWindowLayoutsRepeat();
-
     void _createMessageWindow();
 
     void _hotkeyPressed(const long hotkeyIndex);
@@ -90,6 +83,5 @@ class WindowEmperor : public std::enable_shared_from_this<WindowEmperor>
     {
         winrt::Microsoft::Terminal::Remoting::WindowManager::WindowCreated_revoker WindowCreated;
         winrt::Microsoft::Terminal::Remoting::WindowManager::WindowClosed_revoker WindowClosed;
-        winrt::Microsoft::Terminal::Remoting::WindowManager::QuitAllRequested_revoker QuitAllRequested;
     } _revokers{};
 };
diff --git a/src/cascadia/inc/ControlProperties.h b/src/cascadia/inc/ControlProperties.h
index a882cd8b0cf..0c4a50308d3 100644
--- a/src/cascadia/inc/ControlProperties.h
+++ b/src/cascadia/inc/ControlProperties.h
@@ -56,6 +56,7 @@
 #define CONTROL_SETTINGS(X)                                                                                                                              \
     X(winrt::hstring, ProfileName)                                                                                                                       \
     X(winrt::hstring, ProfileSource)                                                                                                                     \
+    X(winrt::guid, SessionId)                                                                                                                            \
     X(bool, EnableUnfocusedAcrylic, false)                                                                                                               \
     X(winrt::hstring, Padding, DEFAULT_PADDING)                                                                                                          \
     X(winrt::hstring, FontFace, L"Consolas")                                                                                                             \
diff --git a/src/inc/til/small_vector.h b/src/inc/til/small_vector.h
index a88927e7159..f078d37fbe4 100644
--- a/src/inc/til/small_vector.h
+++ b/src/inc/til/small_vector.h
@@ -577,18 +577,26 @@ namespace til
 
         void resize(size_type new_size)
         {
-            _generic_resize(new_size, [](auto&& beg, auto&& end) {
+            _generic_resize(new_size, [](iterator&& beg, iterator&& end) {
                 std::uninitialized_value_construct(beg, end);
             });
         }
 
         void resize(size_type new_size, const_reference value)
         {
-            _generic_resize(new_size, [&](auto&& beg, auto&& end) {
+            _generic_resize(new_size, [&](iterator&& beg, iterator&& end) {
                 std::uninitialized_fill(beg, end, value);
             });
         }
 
+        void resize_and_overwrite(size_type new_size, auto op)
+            requires std::is_trivial_v<T>
+        {
+            _size = 0;
+            reserve(new_size);
+            _size = std::move(op)(_data, new_size);
+        }
+
         void shrink_to_fit()
         {
             if (_capacity == N || _size == _capacity)
@@ -863,7 +871,7 @@ namespace til
 
             // An optimization for the most common vector type which is trivially constructible, destructible and copyable.
             // This allows us to drop exception handlers (= no need to push onto the stack) and replace two moves with just one.
-            if constexpr (noexcept(func(begin())) && std::is_trivially_destructible_v<T> && std::is_trivially_copyable_v<T>)
+            if constexpr (noexcept(func(begin())) && std::is_trivial_v<T>)
             {
                 _size = new_size;
 


Test patch: