Skip to content

Vibeshine 2.13.3 (Nightly)

Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 13 Nov 18:51
· 0 commits to master since this release

This is a nightly release of Vibeshine and contains several bug fixes and may contain new features. Check the commit log for more information.

Windows installer · Linux (Arch x86_64)

Nightly Windows installers are unsigned and include self-signed gamepad components. They may cause false-positive antivirus alerts.

Commit log

Source: 183816054694

Since the preceding successfully published nightly 2.2.5: compare exact sources.

b4303f173236

fix(cmake): Search OpenSSL's headers before Homebrew's prefix

include_directories(BEFORE) prepends each entry in turn, so Opus's
include directory was searched before OPENSSL_INCLUDE_DIR. On macOS Opus
resolves to /opt/homebrew/include, which on the macos-15 runner holds
OpenSSL 4's headers, while CI links a static OpenSSL 3.6 from
OPENSSL_ROOT_DIR. crypto.cpp then took the OpenSSL 4 path and failed to
link ASN1_STRING_get_length. List OpenSSL after Opus so the headers match
the library that's linked.

(cherry picked from commit 9d12ec6ae940157760ff6697d29bd19061cd8702)

f765b3f39be4

fix(macos): Build the paused app monitor with util::jthread

Xcode 16's libc++ provides std::jthread and std::stop_token only behind
-fexperimental-library, so the macOS runners couldn't build
paused_app_monitor.h. Use util::jthread from jthread.h, as the rest of
the code does. The shim's stop_token can't interrupt a
condition_variable_any wait, so wait on a plain condition variable with
a predicate that checks the token, and take the lock in stop() before
notifying so a worker can't miss the wakeup.

(cherry picked from commit c4afdc9c65d63c074ad6f6e77b9bb4c660970202)

530f719d1b82

fix(main): run Windows teardown when startup returns early (#699)

A shutdown request that lands during startup (for example the service
stop Windows sends while powering off) makes main() take one of its
early returns, which skipped the ordered teardown at the end of main.
The virtual display lease thread started by proc::refresh() was then
still joinable when CRT on-exit handlers destroyed it, so the process
aborted (0xc0000409) on every such exit, and the Web UI reported a
crash after each reboot.

Run the same steps from a fail guard on early returns: stop the
deferred virtual-output and recovery workers, the display helper
watchdog, close the virtual display device, and stop the task pool.
The normal path still performs them in order and disables the guard.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 29e9797dce5e631d2ccb8640050694aefdb76e74)

13ce343d8e2f

fix(macos): build with Xcode versions that lack std::jthread

Apple's libc++ has no std::jthread or std::stop_token before Xcode 16.3,
and only behind -fexperimental-library after that. macOS 14 build hosts
top out at Xcode 16.2, which building for macOS 14 with Homebrew's
Sonoma libraries needs.

Add util::jthread and util::stop_token: the standard types wherever
they exist, and small stand-ins otherwise, covering what the code uses
(passing a stop token, stopping and joining on destruction or
assignment). Use them in the places macOS builds compile.

(cherry picked from commit 22c0a146a5fdb528e25cd4cf676cf0ff27014323)

f1d3762ae589

test: restore terminal composition HDR stub (#725)

(cherry picked from commit 73bc025c46cfb26ecda8d1c239abfebbdd414dd9)

6a9b7df3a141

fix(wgc): synchronize activity cadence updates

The game-activity worker could race helper-pipe teardown and report failed cadence writes as applied. Serialize pipe lifetime and sends, force reinitialization after write failure, and publish helper FPS generations with an even/odd snapshot protocol so frames never combine a new cap with stale admission state.

Generated with [Codex](https://openai.com/codex/)

Model: GPT-6 High Standard
(cherry picked from commit b7e4b2105f4435ab113782f893c9a77649678b69)

64e44d1bd339

fix(wgc): preserve concurrent startup cadence

Helper startup could replay a captured FPS after the activity worker had already applied a newer transition. Reconcile the current desired rate under the pipe-lifetime mutex and cover the ordering with a controlled concurrent source replay.

Generated with [Codex](https://openai.com/codex/)

Model: GPT-6 High Standard
(cherry picked from commit f2376ff24374e6850df50f93429022ae426834a6)

203a2afa8534

fix(linux): request SDR displays for synthesized HDR sessions

Vibepollo normal and Remote Monitor topology requests used the encoded HDR flag as a source-display setting. TrueHDR needs SDR capture, so pass the source HDR requirement to those display coordinators while leaving the stream colorspace request intact.

Co-authored-by: Codex <noreply@openai.com>
(cherry picked from commit 4767e1fab1c2b84d08b6a4fdc42ec32f7efe9e03)

960d5558108f

fix(linux): preserve greeter baseline across handoff

Keep one process-owned Vulkan instance alive until PyroWave streams and probes unwind, then release it under the machine-host shutdown deadline.
Carry pending greeter layout through desktop startup by validating same-boot generation, physical monitor identity, and mode even when the compositor has retired its virtual connector.
Exercise handoff recovery and the target-specific machine-host contract with behavioral tests.

Co-authored-by: Codex <noreply@openai.com>
(cherry picked from commit 5a7e9503606ed3c05aaf3dc5da9961b86a782b2f)

e0aee25567ed

fix(linux): preserve cancellation and verify exact display modes

(cherry picked from commit 098d9b48000d67935aa842f4bb6e77a775aeb149)

a52a2052623b

fix(playnite): back off and rate-limit pipe reconnects (#689)

* fix(playnite): back off and rate-limit pipe reconnects

When the pre-0.5 script connector (SunshinePlaynite.psm1) is still
installed next to Playnite.DesktopApp.exe, Playnite loads it alongside the
compiled plugin. Both serve the single-instance pipe
Sunshine.PlayniteExtension, so the one that does not own the name gets
ERROR_PIPE_BUSY from NamedPipeServerStream immediately. The accept loop
retried with no delay and logged a WARN every ~1 ms to extensions.log and
sunshine_playnite-*.log, growing them by gigabytes and keeping Defender's
real-time scanner busy.

Plugin:
- Back off after a failed accept attempt: 250 ms doubling to 10 s with
  jitter, reset on a successful accept; the wait ends on Stop().
- Log the first failure of a run, then at most one summary per 60 s, and
  note recovery once a client is accepted.
- Explain ERROR_PIPE_BUSY: another process owns the pipe, usually a second
  copy of the connector.
- At startup, warn about any SunshinePlaynite.psm1 in Playnite's
  Extensions folders.

Sunshine (IpcClient, also used by playnite-launcher):
- Replace the fixed 500/300 ms reconnect sleeps with the same backoff; it
  resets only after a connection stays up for 5 s, so a connect-and-drop
  loop cannot spin either. Sleeps return promptly on stop().
- Rate-limit connect-failure logging the same way, with the Win32 error.
- Add NamedPipeFactory::set_quiet_connect_failures() so per-attempt
  "pipe not found/busy" logs drop to debug for callers that report
  failures themselves, and last_connect_error() so the real error is
  reported instead of a GetLastError() clobbered by logging.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(playnite): update pipe fixtures for the reconnect backoff

The identification and virtual-display transport fixtures extract code from
pipes.cpp and playnite_ipc.cpp, so they broke on the new connect_to_plugin
signature and the factory's quiet-failure state. Carry that state into both
fixtures and assert the connect error is reported on failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Vibeshine adaptation: preserve the existing ConnectorRetryPolicy server
backoff and logging semantics, private IPC security contracts and their tests.
Port native client backoff and legacy connector warning; extend the native
fixture to check jitter bounds, exponential growth, cap and reset.

(cherry picked from commit b739553a9653fa1b2a288f7e3ab2321ab1a0c3a4)

dc2e8e6e7bc9

fix(tests): keep extracted recovery fixtures aligned with production

Record shell-refresh requests in the boot restore fixture and assert notification behavior without invoking Windows APIs.
Include the production jthread compatibility types in shutdown replay fixtures.

(cherry picked from commit 9316bf90e9f64204061e07964836e2d6f64d26ae)

a29804b23b57

test(ipc): bound pipe authority extraction to its function

Extract only the verified acceptance function so unrelated reconnect-policy methods cannot leak into its standalone security fixture.

Co-authored-by: Codex <noreply@openai.com>

bce0a21d5c5c

fix(macos): restore the macOS build

The macOS build had bit-rotted since CI stopped building it:

- nvenc: include <cstring> for the std::memcmp in equal_guids; libc++
  doesn't pull it in transitively.
- macos: Boost.Process v2 renamed start_dir to process_start_dir.
- macos: DNSServiceRegister needs a C string, and SERVICE_TYPE is now
  a string_view.
- process: include the header-only lossless scaling policy on every
  platform.
- steam, lutris: libc++ counts file times in __int128, which
  std::to_string has no overload for. Convert them to 64 bits first;
  Windows and Linux already count in 64 bits, so their fingerprints
  don't change.
- webrtc: declare the output override lease on macOS too. The
  non-Windows branch already assigns it.
- cmake: let CMAKE_IGNORE_PREFIX_PATH drop /opt/local from the macOS
  link directories, so a stale MacPorts can't shadow the SDK's curl
  and iconv.

(cherry picked from commit 3f5b005f03706cf23991167a07a39423bc3e6922)

8808e2da86dd

fix(macos): make the .app bundle run

- Anchor the working directory to Contents/MacOS at startup, as
  upstream Sunshine does. Bundle assets are referenced relative to it
  (../Resources/assets), so launching from Finder couldn't find
  apps.json and exited.
- Copy the macOS assets (apps.json) into the build tree, so the
  build-tree .app runs without installing, like the Homebrew build.
- Declare NSLocalNetworkUsageDescription, as upstream does. Without it
  macOS can deny the Bonjour registration (kDNSServiceErr_PolicyDenied)
  without asking, and Moonlight can't discover the host.

(cherry picked from commit ac513f50b8515fed380f461fb7219779cbcf178a)

ea026cfdfb9f

build(macos): repair signed DMG and bundle packaging

- Copy the icon into the build-tree bundle too: MACOSX_BUNDLE_ICON_FILE
  only writes the Info.plist key, so the build-tree app showed a blank
  icon (and System Settings resolved to it).
- Fix the DMG: its volume icon pointed at sunshine.png, which no longer
  exists, and the Finder layout script looked for Sunshine.app.
- Drop the versioned executable name inside the .app. codesign rejects
  a symlinked main executable, and install() shipped both files.
- Only use hardened runtime and timestamps with a Developer ID. Ad-hoc
  and self-signed identities have no Team ID, so library validation
  refused to load the bundled dylibs and the app died at launch.
- Also sign the build-tree bundle when APPLE_CODESIGN_IDENTITY is set.
  With a stable identity, macOS privacy permissions (Screen Recording,
  Accessibility) survive rebuilds instead of silently resetting.

Adapted for Vibeshine: retain its existing Sunshine icon and project identity.

(cherry picked from commit 0ebc8ace6d3ceb06076e2aef4b78e7636017725f)

e0b336116545

feat(macos): menu bar icon

The macOS tray was never implemented because AppKit only works on the
main thread. main() now runs the AppKit event loop there once startup
is done, and src/platform/macos/tray.mm implements the tray API on top
of it.

It replaces third-party/tray/src/tray_darwin.m, whose tray_exit() calls
[NSApp terminate:] (skipping Vibepollo's shutdown) and whose
tray_update() touches AppKit from whichever thread calls it. Updates
are copied and applied on the main thread, and tray_exit() just wakes
the event loop. System quit requests (logout, a permission prompt's
"Quit & Reopen") go through the normal SIGINT shutdown.

The icons are the web UI's SVGs used as template images, so they stay
crisp and follow light and dark menu bars.

(cherry picked from commit e14e18a8744729f0cd04d3b3877d55bac5d7ed1e)

a13e1282ae5d

feat(macos): request Accessibility for remote input

Remote mouse and keyboard input is posted with CGEventPost, which macOS
drops unless the app is allowed under Privacy & Security >
Accessibility. Nothing requested it, so no prompt ever appeared and
input from clients was silently ignored. Request it at startup, like
upstream Sunshine, and log why input is ignored until it's granted.

(cherry picked from commit 7ffe635c49b0352c83a9483cf97d096d0488ecb2)

ed21fd9f46f7

feat(macos): per-client virtual display

Brings Vibepollo's virtual display to macOS, driven by the same
settings as on Windows:

- virtual_display_mode: each stream gets a display at the client's
  resolution and refresh rate, created before input and capture bind
  to a display and removed when the stream ends. Concurrent streams
  share it.
- virtual_display_layout: exclusive turns the physical displays off
  while streaming; the primary layouts make it the main display.
  macOS keeps displays adjacent, so the isolated layouts behave like
  their plain versions.
- dd.virtual_display_scale_percent: resolution-based picks Retina (2x)
  above 1920x1200, e.g. phones at native resolution, 1440p, and 4K.

Displays come from the private CGVirtualDisplay API, and physical
displays are turned off with CGSConfigureDisplayEnabled. Both are
looked up at runtime, so if macOS drops them, streams fall back to the
physical display.

macOS does not turn a display back on when the process that turned it
off dies, even with app-only configuration scope. So Vibepollo starts a
helper (itself, with --macos-display-restore-watchdog) that restores
the displays if the main process exits without doing so, and keeps a
marker file that the next launch also restores from.

AVCaptureScreenInput delivers no frames from virtual displays, so they
are captured with ScreenCaptureKit (SCVideo, an AVVideo subclass).
ScreenCaptureKit only sends images when the screen changes, so idle
ticks re-send the last one to keep a steady cadence for keyframe
requests. Physical displays keep using AVCaptureScreenInput.

Capture and input follow the virtual display while it exists.
Display configuration needs an AppKit session, which the main-thread
event loop provides.

(cherry picked from commit a0dcfd800dc3459c074d444787caf763e8e098a8)

699819716a5f

fix(macos): keep virtual display streams alive on a static desktop

macOS streams use the sync encode path, which only encodes frames the
capture delivers and has no minimum frame rate of its own. The old
AVCaptureScreenInput delivered frames continuously, but ScreenCaptureKit
only sends images when the screen changes and can go silent on a static
desktop: tonight's sessions dipped to ~12 fps, below the configured
minimum_fps_target of 20, and a keyframe request after packet loss would
wait until something moved.

Deliver new images as soon as they arrive, and re-send the last one when
nothing new arrives within the minimum frame time, matching the async
encode path's minimum_fps_target handling.

Measured at 2442x1290 Retina @ 120 Hz on an M1 Pro (capture plus
hardware HEVC encode):

                   motion fps  capture->encoded  static fps  static CPU
  passthrough           ~108   12.4 ms avg        18-49, can stall  3.6%
  paced to 120 fps       120   23.7 ms avg       120          17%
  this change           ~108   12.2 ms avg        19.9          3.8%

Pacing every frame was rejected: at this size the hardware encoder is
near capacity, so repeats queued new images and added ~10 ms latency.

(cherry picked from commit 7ae36550494cb66934f6f9785a06c00870f20a0c)

f215688938a1

feat(macos): report GPU utilization in session stats

The stats dashboard showed no GPU usage on macOS. Apple GPUs publish
their utilization in the IORegistry (IOAccelerator's
PerformanceStatistics, "Device Utilization %"), readable without
entitlements, so sample it for host_gpu_percent. The hardware video
encoder is a separate engine and isn't included; GPU encoder
utilization, temperature, and VRAM stay at their sentinels.

(cherry picked from commit 8750c2554f8c738f84a8768795db1c058555a635)

deebe23ebe3c

fix(macos): stream from a sleeping Mac without hanging

A client connecting to a sleeping Mac only brings it into a dark wake,
with every display off. The encoder probe then waited forever for a
frame from the sleeping display (upstream's FIXME), and every later
connection queued behind it until Vibepollo was restarted.

- Wake the displays before capture, as the Windows capture powers its
  display on, and keep them awake while capturing.
- Bound every wait for a frame: the probe gives up after 3 seconds, a
  running capture re-sends the last image after a second without a new
  one, and a ScreenCaptureKit stream that stops by itself is restarted.
- Keep frame callbacks from touching a capture that already returned,
  and stop capture sessions before releasing their callback tables.

(cherry picked from commit 11b0335eb1f3c891a3d71618be521121085383ac)

20b514b0b2d6

fix(macos): turn displays back on even if one has vanished

The exclusive layout turns off every other display, other apps' virtual
displays included. If one of those was removed while off, re-enabling
the list in a single configuration failed as a whole, so every screen
stayed off, and the restore helper failed the same way after a crash.

Turn each display back on in its own configuration, and count a display
that no longer exists as restored. Wake the Mac before restoring, since
a stream can end during a dark wake. Also wait for the turned-off
displays to be gone before capture starts: a capture set up while they
are still going away can get no frames.

(cherry picked from commit dd54af3c4ef9cfead3e643530531fa24fa734826)

e4d0f54385c4

feat(macos): bring the virtual display up when a client connects

Create the per-client virtual display at launch and resume, before the
encoder probe, as the Windows display helper applies the client's
display first. A Mac without a screen of its own, such as a headless
Mac mini, now has a display to probe and stream. The stream's session
takes the display over. A launch that fails removes it, and one that
never starts a stream has it removed after 30 seconds.

Capture setup on a display with no mode now fails cleanly instead of
crashing, so the startup probe on a Mac with no screen can't crash.

(cherry picked from commit 6de88b1659e6eb18de187b8bd101c6d8c568950a)

a0a2a5c8a99d

feat(macos): end streams cleanly when the Mac sleeps or its lid closes

When the Mac is about to sleep, as from Apple menu > Sleep or a low
battery, end active streams with the normal end-of-stream message
before letting it sleep. Clients previously just saw their stream
freeze. The app keeps running, so a client can resume once the Mac
wakes.

Closing a MacBook's lid mid-stream quits the app, which ends its streams
the same way and shows the app as closed. While the virtual display is
the only display, macOS would otherwise keep a plugged-in Mac streaming
with the lid shut, but only for streams started with it open, and a
closed Mac can't be woken back into a stream.

(cherry picked from commit f6b27c29cf8ed1293a98f957f8664dc9a9b66291)

604f77460b42

fix(macos): retain exact launch display refresh policy

Adapt imported display setup to the current millihertz fields and saturating Hertz fallback instead of the removed source normalization helper.

4ec4c76bc786

fix: build without warnings on macOS

The macOS CI builds with -Werror. Clear the warnings the shared code
raised there, mostly state only Windows, Linux, or the WebRTC
transport use:

- Compile the WebRTC-only constants only with WebRTC.
- Keep the RTX Video HDR keys and the startup probe's shutdown capture
  inside their Windows blocks.
- Mark the display flags only Windows and Linux read [[maybe_unused]].
- Give non-Windows builds a no-op Vulkan HDR layer hook instead of
  guarding each call to it.
- Drop lambda captures that were never used, and the constants and
  cookie timestamp nothing read.
- Make kMaxDisplays an enum, so C code can size arrays with it.

(cherry picked from commit c7989ae563e2b7461e8ec263eb718b5d4b145f05)

58defb32f4a1

fix(macos): fall back to PNG menu bar icons

If an SVG tray icon doesn't load, use its 45 px PNG rendering instead
of showing the tooltip text in the menu bar.

(cherry picked from commit 7b73e74b8fa87030f560b41794991e7b2e3bfa8d)

717b1d0cecb9

fix(macos): build confighttp with Apple Clang 15

Capturing a function reference in nested lambdas trips up Apple Clang 15
(Xcode 15.4, the newest on macOS 14 build hosts). Take the blocking
route handler by value, so function handlers become plain pointers.

(cherry picked from commit 08ccf876b81c9cca693b15ebc89e9d8dd5842ade)

a1317f48c532

fix(release): validate and order historical nightly revisions

Adapt legacy parser and metadata compatibility portion only, preserve numeric producer/workflows.

Co-authored-by: Codex <noreply@openai.com>

(cherry picked from commit 0a2aad994bbab95e117c8fe100c2c6f328d5817d)

8efbe86699e9

fix(macos): load tray fallback from current Shine assets

The active web asset set provides 16px PNG states, not the legacy 45px filenames.

Co-authored-by: Codex <noreply@openai.com>

4b7b8eeb5c05

feat(macos): open at login from the menu bar

Add an "Open at Login" checkbox to the menu bar menu. It registers a
launchd agent shipped in the app bundle (Contents/Library/LaunchAgents)
with SMAppService, so launchd starts Vibepollo at login and restarts it
if it crashes. Quitting normally (exit status 0) leaves it stopped
until the next login. It's listed under Allow in the Background in
System Settings > General > Login Items.

The checkmark shows the state macOS reports, so it stays correct when
the setting changes in System Settings. If the user turned Vibepollo
off there, only they can turn it back on, so checking the box opens
Login Items for them.

Only launchd's copy runs while Open at Login is on:
- Opening Vibepollo any other way kickstarts the agent and exits once
  the agent has taken over. launchd only starts the agent for the build
  that registered it, so after an update this copy runs instead, and
  turning Open at Login off and on again registers the new build.
- A lock file keeps a second copy from starting alongside the first.
  The agent asks any other copy to quit and waits for it, which also
  covers turning Open at Login on.
- Unregistering the agent makes launchd stop it, so turning Open at
  Login off from the agent first opens a normal copy, which waits for
  the agent to quit.

(cherry picked from commit 582aac0743257b76848f0431e8216eb5862d6c04)

31a58595573f

fix(macos): bundle libraries that linked dylibs load through @rpath

Homebrew's libwebp, linked for Steam artwork, loads libsharpyuv through
@rpath, which fixup_bundle can't resolve on its own, so packaging the
DMG failed. Pass it the directories of the linked dylibs to search.

(cherry picked from commit 91efc4d84f447f0c774cad3fa002787b7162f7c3)

381e288564d6

fix(macos): say that Screen Recording is missing when no encoder works

Encoders are validated on a captured frame, so without the Screen
Recording permission every encoder fails at startup, and the log blamed
the GPU driver. Name the missing permission and where to allow it
instead, and point the startup warning at today's System Settings
instead of System Preferences.

(cherry picked from commit 3d3c2c61fe9054a82f39a5c7201064417eb601c0)

663bc1d8117b

fix(macos): ask for Screen Recording through ScreenCaptureKit

On macOS 27, CGRequestScreenCaptureAccess() neither prompts nor lists
the app under Privacy & Security: tccd answers "does not allow
prompting", so a new user had no Vibepollo entry to switch on. Asking
ScreenCaptureKit for the shareable content still shows the system
prompt and lists the app, and does on every macOS Vibepollo supports.

(cherry picked from commit 08b5fffc839e504acc54ba9f1ba66a54fddbafa3)

d2743a45fe9d

feat(macos): Remote Monitor

Launching Remote Monitor from Moonlight on another device now gives it
an extra display beside the Mac's, as on Windows and Linux, instead of
being rejected. macOS had no remote display topology backend:

- Each Remote Monitor client gets its own virtual display, named after
  the client and with a serial from its UUID so macOS remembers its
  settings. The topology arranges it around the displays already
  active, and captures it by its display ID.
- Capture uses ScreenCaptureKit for these displays, which
  AVCaptureScreenInput can't capture, and the stream's own virtual
  display no longer overrides them.
- Input sends a Remote Monitor stream's pointer to its display, found
  by the origin its capture started at, and lets the pointer onto
  Remote Monitor displays alongside the streamed one.
- The exclusive layout leaves Remote Monitor displays on, and Remote
  Monitor and Remote Input streams no longer get the game's virtual
  display.

(cherry picked from commit a7268bc2fcf2a35d677519cd7db452918addf093)

1da5feef7182

fix(macos): keep the Mac's display scaling when adding a virtual display

macOS keeps display settings per combination of displays, so bringing
up a virtual display gave the Mac's own displays their default modes:
a Retina screen set to More Space went back to Default for as long as a
stream or Remote Monitor lasted. Turning the displays back on after the
exclusive layout, with a Remote Monitor still present, did the same.
Put back any mode that changed, for this process only, so the user's
own settings are what remain afterwards.

(cherry picked from commit 7a09243d89ce2ca17877a19ca069605d20a16910)

a91e26ec1c51

fix(macos): propagate Remote Monitor release result

Match the current monitor runtime contract and preserve failure reporting instead of dropping the coordinator result.

3ac7fc3a9043

fix(macos): stream black video for Remote Input

Remote Input still needs a video stream, but macOS had no source of
black frames, so the session failed with "Synthetic black capture is
unavailable for the selected encoder backend". Add one that feeds a
black image to the VideoToolbox or software encoder. VideoToolbox also
emits asynchronously, so drain it after the input-only session's only
frame, as AMF is drained.

(cherry picked from commit 77f115677d5fc908e3b1f0e00c7c28e05a02097b)

e130db7597d9

fix(macos): entitle Developer ID builds to capture audio

Signing with a Developer ID enables the hardened runtime, which blocks
audio input unless the app has the audio-input entitlement. Sign the app
with an entitlements file granting it, so audio device capture and the
system audio tap keep working in signed and notarized builds.

(cherry picked from commit 711050ae9739dfa797e826a0f9b7494390b8262c)

73d3fce921d8

fix(macos): ask for one privacy permission per launch

macOS shows one privacy prompt at a time. Vibepollo asked for
Accessibility and Screen Recording together at launch, so one of the
prompts was dropped and needed an extra restart to come back.

Ask for Screen Recording first, and for Accessibility once Screen
Recording is allowed. Allowing Screen Recording needs a restart anyway,
which macOS offers, so each launch shows exactly one prompt.

(cherry picked from commit cefd55e45c012bc7f2ddcf86513c092dde9cf319)

9b00de2552e9

feat(macos): stream HDR

When a client asks for HDR, make the per-client virtual display an HDR
display and capture it in HDR, as on Windows.

- The virtual display: macOS 27's private CGVirtualDisplayMode takes a
  transfer function, and 1 makes the display HDR, with 5x EDR headroom
  and a Display P3 gamut. Remote Monitor displays use it too. Where the
  API is missing, displays stay SDR and so does the stream.
- Capture: ScreenCaptureKit's HDR capture (macOS 15) delivers 10-bit
  frames already in BT.2020 with the PQ transfer function, so they reach
  VideoToolbox without conversion. A physical HDR display, such as a
  MacBook Pro's, is captured in HDR the same way.
- The display reports HDR and its mastering metadata, so the shared code
  encodes HEVC Main10 HDR and sends Moonlight the HDR metadata. SDR white
  measures about 140 nits in the captured frames, so the peak is the
  display's EDR headroom times that, at most 1600 nits.

(cherry picked from commit 6dd718d3f2c10800764f8690e8f5d4e178b743f9)

3b3c339d9772

feat(macos): keep displays off while a session is paused

Hold the virtual display, and the physical displays the Exclusive layout
turned off, while a game keeps running after its stream ends, as on
Windows. Governed by dd_paused_virtual_display_timeout_secs, and
controlled by the new virtual_display_keep_while_paused option (on by
default).

(cherry picked from commit e803b537062584bf2aef26a9d80edbfffda33f55)

60534a99dadd

fix(macos): end the paused display hold when the game is terminated

Quit App on a paused session has no stream left to finalize, so nothing
released the hold and the displays stayed off. Release it in
proc_t::terminate() where Windows removes its own virtual display, with
the same check: when another stream still owns the runtime, the final
stream's cleanup releases it instead.

(cherry picked from commit 1f2a0aeb487a4419b1a1fcdef0f73cee46f434f1)

7a2f119514b6

fix(macos): use numeric Apple bundle versions

Keep the full prerelease version in application metadata while using the existing numeric allocation for Apple bundle version fields.

01d3aeb78a43

fix(macos): give the displays back to Remote Input when a paused game's stream ends

The pause hold kept the virtual display, and so the physical displays the
Exclusive layout turned off, even while a Remote Input or Remote Monitor
stream was still connected, so that client drove a screen no one could see.
Skip the hold while one of those streams is live, end it when one starts,
and only let the stream that owns the virtual display take it.

(cherry picked from commit e320e08b2c72bab5c573671c2645619501b4f22f)

12578c999bbf

feat(macos): keep streaming across lid transitions when the Mac stays awake

Closing a MacBook's lid always quit the app and ended its streams, even
when another utility kept the Mac awake. A new setting, "End streams when
the lid closes" (lid_close_ends_streams, on by default), keeps that
behavior; turned off, the lid alone ends nothing. Vibepollo still doesn't
keep the Mac awake or change power settings, and actual sleep still ends
streams through the existing sleep handler.

Reopening the lid brought the built-in display back next to the virtual
display despite the Exclusive layout. A debounced display reconfiguration
watcher now turns displays that come back on during an exclusive session
off again, on a background queue. It extends the marker file and restore
helper to cover them before turning them off, records their modes for the
end of the session, and gives up after three corrections a minute. It also
puts Vibepollo's own displays back in their created modes when a display
arrives or leaves. Displays that are offline when a session ends, such as
the built-in display with the lid closed, no longer block restoring the
rest; they stay in the marker in case they return turned off.

Capture now logs why ScreenCaptureKit stopped, sets itself up again within
the same session when its display stays offline or changes size, and ends
the stream after ten restarts in a minute instead of retrying forever.

The decisions live in display_policy.cpp, without Apple frameworks, and
have unit tests.

(cherry picked from commit bafbb75f189f8fe732ccd527f2f111373768d0dc)

3d210f931a8c

Turn the built-in display back on when a stream ends with the lid open

Restoring displays skipped any display CGDisplayIsOnline() reported as
offline, assuming that meant a closed lid. But macOS also reports a
display that's only turned off as offline, so ending an Exclusive stream
left the built-in display dark until Vibepollo restarted and used the
marker file.

Only the built-in display while the lid is actually closed is skipped
now, read from AppleClamshellState. Anything else is turned on as before.

(cherry picked from commit 9bedcf84b48904f230536996c25481127bab7b42)

4484f1aaa4aa

fix(macos): retain Shine identity in lid policy documentation

eace30265e44

Turn a display off as soon as it comes on during an Exclusive stream

Opening the lid made the built-in display the main one, with the stream's
display beside it, for as long as Vibepollo waited for the changes to
settle and then for the old restore helper to exit. A display coming on
now gets turned off right away, before the settled pass that fixes modes
and catches anything missed; the old helper is retired after the display
is off, since the new one already covers it. The correction budget is
six a minute, as one lid opening can take two.

Log when the lid opens and how long turning the display off took, to see
how much of macOS's own rearranging remains.

(cherry picked from commit 560ee5068c11f7c10ca5696705178311eb1cac26)

c0d5c9b0e4f4

Wait for the built-in display to wake before dropping the virtual display

When a stream ended, the virtual display was destroyed as soon as the
built-in display was active, even if it was still asleep. With nothing
else on, macOS marks the display stuck and its watchdog restarts
WindowServer. Count a physical display as back only once it is active
and awake.

(cherry picked from commit 843454ea6dc500fc4aa6804211c7a52f1cc947e2)

2ffbc3582c19

Turn the built-in display back on when the lid opens

A stream that ended with the lid closed left the built-in display off,
on the assumption that macOS turns it back on by itself when the lid
opens. It didn't on macOS 27.0.1: the display stayed off with only a
leftover virtual display active.

When the lid opens, run the same recovery that runs at launch: turn on
the displays still listed in the marker. It does nothing while a
virtual display is up, so a paused session keeps the built-in display
off until it resumes or ends.

(cherry picked from commit e914ebc595f8e448cedc1747640f718b987d0f9f)

b9d9df309a82

Document and translate virtual_display_keep_while_paused

The pause hold's setting had no entry in configuration.md or the v2 UI's
base locale, so the config catalog contract test failed, and the v2
settings page didn't offer it. Add the docs entry, the English strings,
and a macOS-only switch next to the other virtual display settings,
appended so the Everyday page's field picks stay the same.

The v2 page also hid the paused virtual display timeout on macOS, which
the pause hold uses as its limit; show it there too and assert that in
the parity test.

(cherry picked from commit 955b265f13907c190ed719dd08b33239635d02d8)

737005eca5c2

Accept pen and touch input from Moonlight clients on macOS

Post the pen as a tablet device: proximity events as it enters, leaves, or
changes tool, and mouse events carrying pressure, tilt, rotation, and the
barrel buttons. The eraser is its own pointer type. Cancel and disconnect
lift every button and leave proximity.

macOS has no public multi-touch injection, so one finger acts as the mouse
(tap to click, move to drag) and two fingers scroll. Advertise pen_touch
unless native_pen_touch is off.

(cherry picked from commit 1bc1b4479bc0b06fd7aca46930768ed04d723253)

c13b294440d2

Post pen pressure changes while the pen holds still and fix touch double taps

Always post a pen move so pressure and tilt changes reach apps, give the
tertiary barrel button its own mouse button number, count double taps for
touch, carry fractional scroll movement between events, and skip the mouse
delta on the first pen event.

(cherry picked from commit 3e21eb2a72021207ecbbd27a5cde7de3a950920e)

8a3c813ac911

Set the mouse event pressure for pen events so NSEvent.pressure follows the pen

(cherry picked from commit 5ff6f9bf36fc7742ff2044319b5802e39112e302)

82a0b42468b0

Scale pen and touch coordinates by the touch port on macOS

The shared input code passes pen and touch positions as 0..1 across the
stream, with the stream size in the touch port, so every event landed at the
top left corner.

(cherry picked from commit c31e381600e2d57b7ccc27279c07930fdd19ddf8)

2cc940c0d2a5

Scroll with one finger and drag after a short hold on macOS touch

(cherry picked from commit d9e7d5931b79a87483ea3fc9babf33dffbd33233)

b0be6f8382d7

Right-click with a two-finger tap or a long press on macOS touch

(cherry picked from commit ac2de5eee9b028d5064bb443c67530664ac51838)

e6dd3d669e7c

Indent touch_release consistently

(cherry picked from commit 40dadaef78cfd7ece9c5256e46ea911f6cf2f65e)

e5e30b0d4cf2

fix(macos): restore the build after merging upstream master

Upstream code that hadn't been built on macOS failed with warnings as
errors: guard the RTX HDR metadata path in PyroWave encoding behind
SUNSHINE_ENABLE_NV_TRUEHDR like the other callers, format a file-clock
count with file_clock::count_string, drop a needless capture of a
static in the signal handlers, mark the stream-start ticket used where
no handler takes it, and route the menu bar library's log to ours.

(cherry picked from commit 89a5df4cc9898b91c552927a5df34de342ab7862)

c1573ec8010b

fix(macos): retain local shutdown signal captures

Shine owns the shutdown atomic as a local rather than the source static, so native signal callbacks must retain their reference captures.

7f3e2247d38e

fix(macos): use util::jthread in the audio role control and its tests

Upstream's audio role control and the audio context and power action
tests use std::jthread and std::stop_token, which the Xcode 15.4 and
16.4 toolchains on the macOS CI runners don't provide. Use the
util::jthread stand-in, as the rest of the code macOS compiles does. It
is std::jthread wherever that exists, so Windows' audio.cpp is
unchanged.

(cherry picked from commit b094e4195d97f2f59ba1f055dd527dbbfdc10344)

8fc6ba62f145

fix(macos): use util::jthread in the display helper's async dispatcher

The macOS build compiles the display helper's dispatcher and state
machine for their unit tests, and the dispatcher's std::jthread and
std::stop_token are missing from the Xcode 15.4 and 16.4 toolchains on
the CI runners. It only uses stop_requested and request_stop, which
util::jthread provides; on Windows util::jthread is std::jthread.

(cherry picked from commit 0d3ca84710bd83e5eea2d02d56c138681dc7d752)

da9cf9d49ebd

fix(macos): address the review of displays, lid close and remote input

Turn displays off only once both safeguards cover them. Writing the
marker now replaces it whole and reports failure, and
cover_and_turn_off() turns nothing off until the marker is written and
a new restore helper runs. The current helper stays until the displays
are off, and for good if anything fails, so a hotplug can no longer
trade a working helper for one that never started. Displays turned off
again while no helper runs get one first.

Keep Remote Monitors that are still being made out of the exclusive
layout. Vibepollo's own displays are recognized by their vendor and
product from the moment macOS makes them, and while one is being made
the layout leaves displays it hasn't turned off before alone, then
enforces itself again once the display is published.

Compose Remote Monitor placement in desktop points. The topology asks
the platform for a client display's footprint, and macOS answers with
the Retina display's size in points, so two 3840x2160 Retina displays
beside a 1920-point display sit at x=1920 and x=3840. Modes are still
reported in pixels.

End Remote Monitor and Remote Input streams when the lid-close policy
quits the app: its exit leaves them running on purpose. A client's own
Quit is unchanged.

Bind each stream's pointer, touch and pen input to its display by ID
instead of by where that display was when capture started. Capture
puts the display ID in the touch port, and the mouse viewport now
carries it too.

(cherry picked from commit 2c72f9d97b2fa2e349209438f14feae394a8d9ed)

dfac74c39816

Use util::stop_token in the private display cleanup policy

Upstream 2.0.1's cleanup policy header takes a std::stop_token, which the
macOS CI's Xcode 15.4 and 16.4 libc++ don't have. The remote display
topology test includes it on every platform, so the macOS build failed.
util::stop_token is std::stop_token wherever that exists, so Linux is
unchanged.

(cherry picked from commit 09869cfad68720e646496af6894e46a98b4f3917)

8ffcdea5e45b

fix(macos): avoid deprecated application activation option

Adapt only the macOS deprecation fix from the source merge resolution, preserving current eligible-window and cancellation budgets.

(cherry picked from commit 20515bc6c7c17af3ab811776a759b47ab7e85256)

ddfa4f0f53b9

docs(macos): keep Shine identity in display safeguards

89cebdfe095f

fix(macos): select verified architecture-matched update packages

Port only the missing macOS package selector from the source updater migration. Preserve Shine package names, numeric allocation, metadata verification and legacy nightly revision compatibility.

(cherry picked from commit e704d1ddf467f1665d66e27b8440f913d430609f)

7b63a254fee2

ci(macos): build Apple Silicon and Intel DMGs for macOS 14.2+

Run the macOS build from the main CI workflow, like the Windows one,
and allow running it on its own by hand. Without a Developer ID
identity, sign ad hoc, so the DMG launches on Apple Silicon. Smoke-test
the packaged app: its signature, the oldest macOS it supports, and that
it starts and serves its web UI and serverinfo.

(cherry picked from commit 6b11e0ac5af93992a30f4428157d4cef976c79c1)

ac63aff461a0

ci(macos): build OpenSSL for the deployment target

The Intel DMG required macOS 15.0 instead of 14.2: the runner image's
OpenSSL targets the runner's own macOS 15, and Homebrew no longer
publishes Intel macOS bottles of OpenSSL to replace it with. Build a
static OpenSSL for MACOSX_DEPLOYMENT_TARGET on both architectures,
cached per version, and fail the smoke test when anything in the app
requires a newer macOS than the deployment target.

(cherry picked from commit 0a2980d866db7a5b3df697d3bbef9e79b2646824)

cb6c803220b2

ci(macos): default the build version to 2.2.0

(cherry picked from commit 0db0d078d1562aa020330448c7304c398d75d45e)

8c685b866a52

ci(macos): build Apple Silicon on the macos-15 runner

GitHub no longer accepts the macos-14 image, so the arm64 jobs were cancelled before they started. MACOSX_DEPLOYMENT_TARGET still pins the minimum to macOS 14.2, and the smoke test checks it.

(cherry picked from commit 6b8a3fe5e4951e52a7ab84e4af796065eb3d2cbc)

02ced0634b71

ci(macos): require macOS 15

GitHub retired the macos-14 image, and on macos-15 Homebrew pours
Sequoia bottles. The bundled ICU, libjpeg-turbo, libpng, libwebp, and
miniupnpc then require macOS 15.0, so the smoke test rejected the app
against the 14.2 deployment target. Homebrew no longer publishes Sonoma
bottles for most of them, so raise the deployment target to 15.0 rather
than build each from source.

(cherry picked from commit 6943ba49eea63c835f8fabd390d52dec578bece6)

9d1557920d5b

ci(macos): quote the deployment target

YAML reads an unquoted 15.0 as the number 15, so the smoke test compared
the app's 15.0 minimum against "15" and failed. Quote it so the
environment keeps "15.0".

(cherry picked from commit 14d96559de1964b90877d0e1802a45d70262e946)

65477e87175e

ci(macos): version builds like Windows and Arch

Pass the release candidate's version and channel to the macOS job, and
fall back to 2.0.0-pr-validation and the legacy channel for pull
requests, as the Windows and Arch jobs do. A manual run resolves its
version the way Windows does: the requested version, else the tag, else
0.0.0. The hard-coded 2.2.0 default would have gone stale with the next
release.

(cherry picked from commit b23a48c99cb1334fd3dc4aa54e4ecfe5731ff241)

0235a9403198

fix(ci): preserve Vibeshine macOS source and validation boundaries

Keep the allocated version and channel, build the requested source, and skip unrelated macOS jobs during hosted Linux validation. Bound smoke-test cleanup and retain Vibeshine artifact identities.

Co-authored-by: Codex <noreply@openai.com>

56e2d94b4dd5

lowlatency fallback for AMF (+ VAAPI lowpower fallback) (#419)

* Update amf_d3d11.cpp - AMF lowlatency fallback

Add lowlatency fallback for amf encoder

* Update video.cpp - VAAPI lowpower fallback

implemented the VAAPI lowpower fallback

(cherry picked from commit e1dd3091d4009bffb6715f3b6d260adaaeb87080)

d2d2f544c52c

perf: port Apollo low-latency/jitter improvements (AP1, AP6, AP9, SP1, SP2) (#281)

Ports the latency- and WiFi-jitter-relevant subset of the Apollo perf-curated
branch onto Vibepollo, adapted to Vibepollo's code structure. DS5 customizations
were intentionally excluded (obsolete via the CTM bridge). Knobs already present
in Vibepollo (min_qp, filler_data, split_encode, weighted_prediction) were skipped.

- AP1: configurable RTP send-pacing bandwidth (pacing_max_bitrate_kbps).
  Replaces the hardcoded ~80%-of-1Gbps pacer that no-ops on WiFi links and lets
  AMPDU aggregation amplify per-frame bursts into 30-60ms inter-arrival spikes.
  0 = legacy. Includes a 1-packet/ms floor.
- AP6: raise videoBroadcastThread + sync/async encode threads high->critical;
  audioBroadcastThread stays high.
- AP9: expose nvenc_temporal_aq with NV_ENC_CAPS_SUPPORT_TEMPORAL_AQ probe.
- SP1: server-side packetsize cap (200..65535) to avoid fragmentation on low-MTU links.
- SP2: harden intra-refresh - add outputRecoveryPointSEI to the HEVC path and add
  the previously-missing H264 intra-refresh block (caps-probe + recoveryPointSEI +
  single-slice). Activates when the client negotiates enableIntraRefresh.

Web-UI strings and docs intentionally omitted; keys are sunshine.conf-only.

Co-authored-by: vibecode <vibecode@users.noreply.github.com>
(cherry picked from commit 90b02a1693a08cbfad24a3bf2c9672c1753b3a65)

7f767e0fe1c7

fix(stream): honor configured video pacing ceilings

Reject nominal video demand that cannot fit an explicit pacing ceiling,
including packet headers, encryption, FEC and rounding. Reserve shared
capacity before session publication or any runtime bitrate mutation instead
of raising the operator's ceiling to an insufficient encoder-only floor.
Keep default-zero pacing unchanged and return client errors for infeasible caps.

Related to Nonary/Vibepollo#543; its original NIC-speed symptom remains unconfirmed.
Generated with [Codex](https://openai.com/codex/)
Model: GPT-6.1-sol X-High Standard

(cherry picked from commit a92d7fd117c32156220d468f2e6916e9fb58288b)
(cherry picked from commit d36ee2c111a07748b8783070e604565ed8205255)

7f937fe4364b

fix(stream): support fractional video pacing rates

Use cumulative datagram bit-time for positive-cap admission and every sender
deadline, allowing feasible rates below one packet per millisecond. Keep
batch sizing independent and preserve explicit ceilings without rate truncation.
Make long positive-cap waits cancellable while retaining zero-cap timing.

Corrects review finding F002; #543's original NIC-speed symptom remains unconfirmed.
Generated with [Codex](https://openai.com/codex/)
Model: GPT-6.1-sol X-High Standard

(cherry picked from commit 0a24ab4bbf1231d263335a4eb1f0cebf25516ff9)
(cherry picked from commit 69bee99633a6378991c74f0ef86a95ee5a78a4bb)

3c9ac3b9c357

test(sync): preserve encoder and pacing compatibility contracts

Keep the existing broadcast control adapter and precise adaptive pacing while applying optional explicit ceilings. Exercise atomic admission, fractional timing, bounded AMF retry and device-loss exclusions, hardware-gated temporal AQ and low-MTU packet caps. Preserve the existing C++ default scheduling priorities and document disabled-by-default options. Release the lifecycle gate before a rejected start can destroy its last broadcast reference.

Co-authored-by: Codex <noreply@openai.com>

9803741545b8

fix(linux): honor managed virtual audio sink

Prefer the sink actually selected for the streaming session over a configured physical sink so managed virtual audio remains capturable and resumable.

(cherry picked from commit f8c2270a5afe54109823bf05fdf299e1316e4529)

aef2c74259c8

fix(linux): preserve stream metadata in brokered app commands

Forward bounded, typed stream metadata and the supported Proton controller toggles to desktop-user prep and undo commands. Validate the exact allowlist before dropping privileges and set literal environment values only in the selected user service, preserving command and working-directory authorization.

Generated with [Codex](https://openai.com/codex/)
Model: GPT 6.1-Sol Ultra

(cherry picked from commit d74b4e9376fc13aa0f3f8fdec79f2498b2f5243b)

0b28712972e5

test(linux): cover managed audio capture sink precedence

Exercise production selection for managed and resumed sinks while preserving configured/default fallbacks. Broker metadata adaptation preserves directory-aware command routing and the isolated elevated path; only supported Sunshine metadata and Proton toggles cross the desktop-user boundary.

Co-authored-by: Codex <noreply@openai.com>

08b4f455306b

fix(ci): restore nightly dependency and regression contracts

65ff782d0d29

fix(linux): make machine-host shutdown deadline unconditional

Exit the supervised machine host before touching logging on deadline expiry.
Keep phase diagnostics and the debug trap on the ordinary host path.
Exercise blocked logging, signal-driven expiry, ordinary expiry and completion
with the extracted production deadline and real thread compatibility wrapper.

Targeted reverse port; preserve Vibeshine shutdown phase tracking.
(cherry picked from commit 02ea43e9abe5b62014f238d07bafb05c26db4217)

d86f4b171d4d

fix(display): fail closed when switching helper engines

Carry the missing timeout and wait-failure returns from the reviewed engine
restart change. Restrict fast-ping reuse and hard restart to a matching engine.
Preserve Vibeshine's private control pipe, expected-process checks and centralized
termination helper. Exercise the production branch with deterministic wait,
connection reset, cancellation and ping outcomes.

Targeted reverse port of the remaining engine-switch guards.
(cherry picked from commit 4944d915f34185b883cfbb263107c65d137ecf8e)

e2183dc1e6ca

fix(display): bound capture retries by the stream-start budget

Publish the current stream-start APPLY deadline and cap post-APPLY capture
retry sleeps by its remaining time. Keep full-policy applies and normal
capture retries unchanged, and preserve current recovery-instance tracking.
Exercise the extracted publication, readback and capture retry code with a
controlled clock, including expired budgets and successful capture.

Targeted reverse port; existing helper/IPC startup budgets remain in place.
(cherry picked from commit 88fbd834c5992bcc2325259704e5922e87a4d080)

89768e76001e

test(windows): register portable policy regressions

Restore the generic Windows policy harness registrations from the reviewed
source and run portable Playnite/color-profile checks outside the Linux gate.
Keep Linux-only fixtures and the existing Windows PyroWave header-order test.
Validate actual CMake-generated registration for Windows and Linux conditions.

Targeted generic registration portion; CUDA release configuration is already present.
(cherry picked from commit 712c60eade33a2610ab49e6c1e6fcb7229736241)

ae231be947a3

test(windows): keep restored policy harnesses portable

Carry the quoted-header search path and Windows CRT guard from the source.
The disconnect fixture has already superseded the source's third hunk.

Adapt the restored checks to Apple's compiler: mark an optional permission
constant unused, select Windows production branches after host C++ headers,
and canonicalize temporary fixture roots before exercising the unchanged
Playnite symlink guard. Explicitly select both readiness branches on Windows.

(cherry picked from commit 8468c3da401eed0dc10641446fafe041116a6830)

5cd63c24be3e

fix(input): default the mouse viewport's output name

Existing callers build a viewport from its position and size alone, and
-Wmissing-field-initializers flags those once the viewport carries the
output name.

(cherry picked from commit 19ed053a3cc258564f750f43110445e6423d789d)

e1bcfb510e09

test(ci): tolerate wrapped CMake diagnostics

baa3b2d064e9

test(ci): refresh Linux production-boundary adapters

183816054694

test: align shutdown and budget fixtures with production

Use forced exit status, bounded elapsed time, vendor entry and absent cleanup
completion as the PyroWave deadline oracle. Exercise normal and probe-return
shutdown with blocked vendor release and a deliberately blocking logger seam;
never require a diagnostic emitted by the unsafe pre-exit logging path.

Guard the capture-budget fixture's Windows macro and compile its production
replay with both default and already-defined platform macros. Load host C++
headers before selecting the Windows branch on non-Windows test hosts.

Preserve the production unconditional shutdown deadline and newer Canary repairs.