Fix plugin assembly resolution for cross-project dependencies
Details
PluginLoadContext now resolves assemblies from sibling plugin directories
when the plugin's own directory and host don't have them. This fixes the
Tasks plugin failing to load in client mode with FileNotFoundException
for PrivStack.Plugin.Tasks.Headless — the Headless DLL is deployed to
its own sibling directory (plugins/PrivStack.Plugin.Tasks.Headless/)
and the Tasks plugin depends on it for shared models and services.
Also guard Emergency Kit regeneration in client mode — SetupRecovery()
requires the native runtime which isn't initialized when running as a
client. Shows a clear message directing users to the server instance.
Fix client mode: no plugin UI and silent logging after workspace switch
Details
Two root causes fixed:
1. PluginRegistry logging silently dead after workspace switch:
The static readonly ILogger field was captured from the old Serilog logger.
When ReconfigureForWorkspace calls Log.CloseAndFlush(), the ForContext child
becomes bound to a disposed logger — all subsequent messages silently vanish.
Changed to a property that always reads from the current Serilog.Log instance.
Added Console.Error fallback in plugin init catch blocks so errors are always
visible regardless of Serilog state.
2. No UI for any plugin in client mode:
MainWindowViewModel.SelectTab() guards on _service.IsInitialized, where
_service is IPrivStackRuntime (native Rust/DuckDB runtime). In client mode
the native runtime is intentionally NOT initialized — the headless server
owns DuckDB and the desktop routes all SDK calls through HTTP transport.
This caused SelectTab to abort with "Not initialized" for every plugin,
preventing any plugin view from being created or displayed.
Added IsServiceReady property that checks either native init OR client mode,
and replaced all 4 guard sites in the ViewModel.
Fix client mode runtime issues: Tasks plugin, API server, and warnings
Details
- PluginRegistry: Skip *.Headless.dll during assembly scan in Desktop mode.
Headless plugin DLLs are server-only but were being loaded by the Desktop's
ScanAssemblyPlugins, creating type identity conflicts and duplicate plugin
IDs (both TasksPlugin and TasksHeadlessPlugin share ID "privstack.tasks").
- SettingsViewModel: Guard ToggleApiServerAsync with App.IsClientMode.
Setting ApiEnabled in the constructor triggered OnApiEnabledChanged which
called StartAsync on the local API server — even in client mode where
the remote server already owns port 9720.
- MainWindowViewModel: Skip SubscriptionValidationService.ValidateAsync
in client mode. The service calls ILicensingService.GetLicenseStatus()
which hits the uninitialized native runtime. License status is already
proxied from the server during client mode detection.
- SyncPairingViewModel: Skip LoadInitialState in client mode. The method
calls IPairingService FFI methods (GetSyncCode, SetDeviceName, etc.)
that require the native runtime. Server handles sync in client mode.
Guard BackupService and vault unlock for client mode
Details
- BackupService: skip scheduled backups in client mode (server handles
its own backups). Previously, SettingsViewModel's DI resolution
triggered BackupService constructor which immediately started the
backup timer — even in client mode.
- PluginRegistry.EnsurePluginVaultsUnlocked: skip in client mode since
vault state is managed by the server. The NativeLib vault calls would
fail (or succeed silently via catch) since the native runtime isn't
initialized in client mode.
Register EmbeddingService and IEmbeddingService in Desktop DI
Details
EmbeddingService (ONNX embedding model) and its IEmbeddingService
interface were never registered in Desktop's DI container. The Server
project registered a no-op HeadlessEmbeddingService, but Desktop was
missing the mapping entirely. This caused RagSearchService resolution
to fail when CommandPaletteViewModel built AI commands during
MainWindowViewModel construction.
Pre-existing bug surfaced by client mode startup where the full
DI resolution chain is exercised earlier than in standalone mode.
Get notified about new releases