Fix Dashboard showing empty when plugin registry API times out
Details
The Dashboard RefreshAsync method called AllPlugins.Clear() before
fetching the remote plugin registry, so when the API call timed out
the entire catch block skipped populating locally installed plugins,
system metrics, and workspace activation state — leaving the UI stuck
showing "0 installed · 0 available" with empty metric cards.
Restructured RefreshAsync to:
- Fetch local installed versions first (never fails)
- Wrap the remote registry fetch in its own try/catch so timeouts
are non-fatal — locally installed plugins still appear
- Always load system metrics regardless of registry availability
Also increased PrivStackApiClient timeout from 15s to 30s since the
plugin registry endpoint can be slow.
Remove N+1 individual entity reads from BacklinkService index build
Details
Phase 2 of BuildIndexAsync already calls WikiLinkParser.ExtractContentFromEntity
on each entity from ReadList. The "load individually" block re-fetched entities
without content via individual SdkAction.Read calls, but the individual Read
returns the same JSON shape — ExtractContentFromEntity produces the same empty
result. This caused 21+ unnecessary contact.Read SDK calls at startup.
Removing the block eliminates the N+1 query pattern with no behavioral change,
since entities that genuinely lack parseable text fields won't gain content from
a re-fetch.
Pre-build backlink index at startup
Details
Adds BacklinkService.PreBuildIndexAsync() and calls it during the
deferred background services block in App.axaml.cs. This eliminates
the ~700ms cold-start delay when the user first clicks an item with
the Info Panel open, since the cross-plugin backlink index is already
warm by the time they interact.
The existing SemaphoreSlim guard in EnsureIndexBuiltAsync ensures
concurrent calls (from a user click racing the startup pre-build)
are safe. If the pre-build fails, the index falls back to lazy
initialization on first query (existing behavior).
Add bare JSON intent fallback and strengthen slot guidance
Details
ParseActionBlocks now falls back to ExtractBareIntentJson when no
[ACTION] wrapper tags are found, recovering intent JSON that the AI
emits without the required tags. The fallback only triggers when the
primary parser found nothing, preventing double-extraction.
ActionFormatHeader now explicitly warns against JSON document objects
in content/description text slots.
Visibility changes: ParseActionBlocks, ExtractBareIntentJson,
ParsedAction, and FlattenSlotValue are now internal (testable via
InternalsVisibleTo).
6 new xUnit tests cover bare JSON recovery, surrounding prose
stripping, non-intent JSON rejection, multiple intents, and
malformed JSON handling.
Add context-aware RAG query augmentation from active entity
Details
When the user asks a question while viewing an entity, the RAG query is
now augmented with semantic keywords extracted from the entity's JSON
(job_title, company_name, department, tags, contexts, etc.). This causes
the embedding search to surface semantically related items across plugins
without any changes to the RAG service or Rust embedding layer.
Get notified about new releases