Skip to content

feat(assistant): add consent-gated diagnostics - #189

Merged
HsiangNianian merged 7 commits into
mainfrom
feat/beta1-assistant
Aug 3, 2026
Merged

HsiangNianian merged 7 commits into
mainfrom
feat/beta1-assistant

Conversation

@HsiangNianian

@HsiangNianian HsiangNianian commented Jul 31, 2026 •

Copy link
Copy Markdown
Member

Summary

  • add a diagnostic assistant workspace for local Ollama and OpenAI-compatible providers
  • route failed launches into explicit, redacted session analysis without attaching logs by default
  • harden streaming, provider health, and session isolation with EN/ZH docs and fixture coverage
  • add opt-in operating-system speech after complete responses without a separate TTS endpoint

Validation

  • cargo test --workspace --no-fail-fast (204 passed on Unix; 205 on Windows; 1 ignored)
  • pnpm generate (159 binding tests passed)
  • pnpm -C packages/ui build
  • pnpm -C packages/ui test:ui (130 passed)
  • pnpm -C packages/docs build
  • pnpm deploy:docs:dry-run
  • pnpm exec prek run --all-files

Closes #35

Summary by Sourcery

Introduce a consent-gated diagnostic assistant workspace that analyzes failed launches using sanitized session evidence and supports local Ollama and OpenAI-compatible providers.

New Features:

  • Add a dedicated Diagnostic Assistant page and navigation entry for troubleshooting launches with chat-based guidance.
  • Enable failed launches to open a recovery workspace that can optionally attach redacted session evidence to assistant requests.

Enhancements:

  • Sanitize and deduplicate assistant log context before use, including redaction of credentials, identifiers, and IPs.
  • Harden streaming of Ollama and OpenAI-compatible responses with robust line buffering, UTF-8 handling, and provider health checks.
  • Require explicit assistant enablement and complete provider configuration before health checks or chat requests are processed.

Documentation:

  • Document the Diagnostic Assistant feature in English and Chinese manuals, including setup, consent model, privacy boundary, and failure flows.
  • Update troubleshooting guides to describe the new "Ask diagnostic assistant" recovery workflow and consent toggle.
  • Refresh feature overviews to replace the generic AI assistant entry with the consent-gated Diagnostic Assistant and provider details.

Tests:

  • Extend UI launcher fixtures and visual/accessibility tests to cover assistant-ready and assistant-offline states and the failure-driven recovery flow.
  • Add backend unit tests for assistant log context handling, sanitization, streaming buffers, and configuration validation.

@sourcery-ai

sourcery-ai Bot commented Jul 31, 2026 •

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds a consent-gated diagnostic assistant workspace wired to sanitized launcher logs, strengthens assistant provider health checks and streaming robustness for Ollama and OpenAI-compatible backends, and documents the feature with EN/ZH guides and fixtures.

Sequence diagram for consent-gated diagnostic assistant with sanitized log context

sequenceDiagram
    actor User
    participant AssistantPage
    participant AssistantStore
    participant Backend as TauriCommands
    participant GameAssistant
    participant Provider as LLMProvider

    User->>AssistantPage: click [Ask diagnostic assistant]
    AssistantPage->>AssistantStore: refreshContext(recentLogs.map(message))
    AssistantStore->>Backend: get_assistant_log_context(lines)
    Backend->>GameAssistant: get_sanitized_log_context(lines)
    GameAssistant->>GameAssistant: sanitize_log_line / sanitize_log_text
    GameAssistant-->>Backend: AssistantLogContext
    Backend-->>AssistantStore: AssistantLogContext
    AssistantStore-->>AssistantPage: update context preview

    User->>AssistantPage: compose prompt
    User->>AssistantPage: toggle includeContext
    AssistantPage->>AssistantStore: sendMessage(prompt, logContext | null)
    AssistantStore->>Backend: assistant_chat_stream(messages, logContext)
    Backend->>GameAssistant: chat_stream(messages, config.assistant, window, log_context)
    GameAssistant->>GameAssistant: validate_config(config)
    GameAssistant->>Provider: POST /api/chat or /chat/completions (stream)
    Provider-->>GameAssistant: bytes_stream
    GameAssistant->>GameAssistant: take_complete_lines / process_line
    GameAssistant-->>Backend: emit("assistant-stream", StreamChunk)
    Backend-->>AssistantStore: assistant-stream events
    AssistantStore-->>AssistantPage: append AssistantMessage, stats
    AssistantPage-->>User: show recovery guidance
Loading

File-Level Changes

Change Details Files
Introduce sanitized assistant log context and stricter provider configuration/health checks, and harden streaming for Ollama/OpenAI-compatible responses.
  • Add AssistantLogContext type and log sanitization helpers to redact secrets, identifiers, and normalize paths/IPs before use in prompts.
  • Track and clear assistant log buffers on game start, combine backend and frontend lines without duplicates, and bound context length.
  • Require assistant enablement and complete provider configuration; implement real health checks for Ollama (tags endpoint) and OpenAI-compatible providers (models endpoint with API key).
  • Refactor Ollama/OpenAI streaming to use a shared byte buffer line splitter that preserves split JSON and UTF-8, emitting structured StreamChunk events with optional GenerationStats.
src-tauri/src/core/assistant.rs
src-tauri/src/main.rs
Expose assistant diagnostics and log context via Tauri commands and TypeScript bindings, and integrate them into the UI client/fixtures.
  • Extend assistant_chat/assistant_chat_stream commands to accept optional sanitized logContext, and add get_assistant_log_context command returning AssistantLogContext.
  • Generate TS bindings for AssistantLogContext and wire new Tauri commands into the web client API.
  • Update launcher fixtures to simulate assistant-ready/offline states, provide deterministic logContext, and emit assistant-stream events for streaming tests.
  • Clear assistant logs when starting a new game session to avoid cross-session evidence leakage.
src-tauri/src/main.rs
packages/ui/src/types/bindings/assistant.ts
packages/ui/src/client.ts
packages/ui/src/fixtures/launcher.ts
packages/ui/src/fixtures/bootstrap.ts
Add a dedicated Diagnostic Assistant page, sidebar navigation, and consent-gated attachment workflow backed by a new assistant store.
  • Create AssistantPage with transcript, evidence preview, per-request Attach to next request switch, and in-place provider setup section.
  • Implement a zustand-based assistant store to manage messages, streaming state, providerHealth, log context loading, and history clearing with robust listener lifecycle.
  • Wire navigation from failed launch overview (Ask diagnostic assistant button) and add sidebar nav entry and router route for /assistant.
  • Use recent launcher logs from game store for context preview, initializing workspace with failure prompt when navigated from a failed launch.
packages/ui/src/pages/assistant.tsx
packages/ui/src/models/assistant.ts
packages/ui/src/pages/home.tsx
packages/ui/src/pages/routes.ts
packages/ui/src/components/sidebar.tsx
packages/ui/src/models/game.ts
Extend UI tests, fixtures, and localization to cover assistant-ready/offline scenarios and consent-gated diagnostics, and update EN/ZH docs for the new feature.
  • Add Playwright fixtures and tests for assistant-ready/offline routes, failed-launch assistant entry, and default-detached evidence behavior.
  • Register new launcher fixture names for assistant-ready/offline and ensure failed/assistant fixtures seed lastError state appropriately.
  • Add EN/ZH locale strings for assistant nav, workspace, setup, context, and prompts (including failure-oriented starter questions).
  • Document Diagnostic Assistant in EN/ZH feature manuals and troubleshooting guides, including privacy boundary and provider configuration, with a new assistant feature page in both languages.
packages/ui/tests/launcher-fixtures.spec.ts
packages/ui/src/lib/launcher-runtime.ts
packages/docs/content/en/manual/features/index.mdx
packages/docs/content/zh/manual/features/index.mdx
packages/docs/content/en/manual/troubleshooting.mdx
packages/docs/content/zh/manual/troubleshooting.mdx
packages/docs/content/en/manual/features/assistant.mdx
packages/docs/content/zh/manual/features/assistant.mdx
packages/ui/src/locales/en.json
packages/ui/src/locales/zh-CN.json

Assessment against linked issues

Issue Objective Addressed Explanation
#35 Implement an in-game assistant feature that can use a local LLM or remote API provider to answer player/launcher questions. ✅
#35 Integrate the assistant into the launcher UI as an accessible in-game guide/diagnostic workspace, including routing from failure states. ✅
#35 Add configuration, health checking, and documentation for the assistant, including provider setup and privacy/log-handling behavior. ✅

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@github-actions

github-actions Bot commented Jul 31, 2026 •

Copy link
Copy Markdown
Contributor

Workspace change through: 9b799a9

1 changesets found

Planned changes to release
Package Bump Level Current Version Next Version
@dropout/ui patch 0.1.0-alpha.7 0.1.0-alpha.8
@dropout/docs patch 0.1.0-alpha.5 0.1.0-alpha.6
dropout patch 0.2.0-alpha.9 0.2.0-alpha.10

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src-tauri/src/core/assistant.rs" line_range="618-622" />
<code_context>
         let mut stream = response.bytes_stream();
+        let mut buffer = Vec::new();
+
+        let process_line = |line: &[u8], full_content: &mut String| -> Result<(), String> {
+            if line.iter().all(u8::is_ascii_whitespace) {
+                return Ok(());
+            }
+            let stream_response = serde_json::from_slice::<OllamaStreamResponse>(line)
+                .map_err(|error| format!("Failed to parse Ollama stream response: {error}"))?;
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Streaming now fails hard on any malformed Ollama line, which may be too strict for mixed or diagnostic outputs.

The previous streaming path tolerated per-line JSON parse failures and continued streaming. With `process_line` now returning `Err` on any `serde_json::from_slice` failure, the whole response aborts if the endpoint emits non-JSON diagnostics or partial lines (e.g., log prefixes before JSON). Please consider either logging and skipping malformed lines to preserve robustness, or limiting strict parsing to endpoints guaranteed to emit only well-formed JSON frames.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src-tauri/src/core/assistant.rs Outdated
Copilot AI review requested due to automatic review settings August 3, 2026 10:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new “Diagnostic assistant” workflow that helps users troubleshoot failed launches using either a local Ollama provider or an OpenAI-compatible provider, with explicit per-request consent for attaching redacted session evidence. It adds backend support for bounded provider health checks and request-isolated streaming, plus a new UI page, fixtures/tests, and EN/ZH documentation.

Changes:

  • Add backend assistant log-context sanitization utilities, configuration validation, provider health timeouts, and cancellable/request-isolated streaming.
  • Add a new UI assistant page + navigation, failure-entry routing, evidence preview/attachment controls, and optional OS speech for completed responses.
  • Extend fixtures, UI tests, bindings, and docs (EN/ZH) to cover the new assistant recovery flow.

Reviewed changes

Copilot reviewed 26 out of 46 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src-tauri/src/main.rs Adds assistant log-context + streaming request lifecycle commands and wires them into the Tauri handler.
src-tauri/src/core/config.rs Updates assistant config comment to reflect the new TTS provider options.
src-tauri/src/core/assistant.rs Implements sanitized log context, config validation, provider health checks, and request-isolated streaming with timeouts.
packages/ui/tests/launcher-fixtures.spec.ts Adds visual/accessibility and behavior coverage for assistant states, failure recovery flow, stream isolation, and TTS.
packages/ui/src/types/bindings/assistant.ts Updates generated bindings for AssistantLogContext and adds requestId to stream chunks.
packages/ui/src/pages/routes.ts Registers the new /assistant route.
packages/ui/src/pages/home.tsx Adds a failure-state CTA to open the diagnostic assistant from the Overview page.
packages/ui/src/pages/assistant.tsx New diagnostic assistant page: transcript UI, evidence preview + attach toggle, provider setup, and speech synthesis.
packages/ui/src/models/game.ts Ensures recent logs are cleared on a new launch reset.
packages/ui/src/models/assistant.ts New Zustand store for assistant health checks, context refresh, streaming chat, and cancellation handling.
packages/ui/src/locales/zh-CN.json Adds Chinese localization for assistant UI and navigation.
packages/ui/src/locales/en.json Adds English localization for assistant UI and navigation.
packages/ui/src/lib/launcher-runtime.ts Adds assistant-related fixtures to the supported fixture list.
packages/ui/src/fixtures/launcher.ts Adds fixture implementations for assistant commands and streaming event emissions.
packages/ui/src/fixtures/bootstrap.ts Extends bootstrap state for assistant-related fixtures and failure-driven entry.
packages/ui/src/components/sidebar.tsx Adds a sidebar navigation item for the assistant.
packages/ui/src/client.ts Adds/updates client wrappers for assistant streaming lifecycle and log-context retrieval.
packages/docs/content/zh/manual/troubleshooting.mdx Documents the assistant recovery workflow from failed launches (ZH).
packages/docs/content/zh/manual/features/meta.json Adds assistant to the ZH features navigation metadata.
packages/docs/content/zh/manual/features/index.mdx Updates ZH features overview to reference the diagnostic assistant.
packages/docs/content/zh/manual/features/assistant.mdx New ZH manual page describing setup, consent model, and behavior.
packages/docs/content/en/manual/troubleshooting.mdx Documents the assistant recovery workflow from failed launches (EN).
packages/docs/content/en/manual/features/meta.json Adds assistant to the EN features navigation metadata.
packages/docs/content/en/manual/features/index.mdx Updates EN features overview to reference the diagnostic assistant.
packages/docs/content/en/manual/features/assistant.mdx New EN manual page describing setup, consent model, and behavior.
.changes/beta1-assistant.md Adds a changeset entry announcing the new consent-gated diagnostic assistant feature.

Comment thread src-tauri/src/core/assistant.rs
Comment thread packages/ui/src/pages/assistant.tsx Outdated
Comment thread packages/ui/src/pages/assistant.tsx Outdated
Copilot AI review requested due to automatic review settings August 3, 2026 11:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 46 changed files in this pull request and generated 1 comment.

Suppressed comments (4)

src-tauri/src/core/assistant.rs:533

  • The OpenAI-compatible chat request builds an invalid Authorization header (format!("******", api_key)) which is both a compile-time error (unused format argument) and would not authenticate at runtime. Use a Bearer auth header derived from the API key.
    src-tauri/src/core/assistant.rs:846
  • The OpenAI-compatible streaming request uses format!("******", api_key) for the Authorization header, which is invalid (unused format argument) and will break streaming authentication. Use bearer auth with the API key.
    src-tauri/src/core/assistant.rs:622
  • The OpenAI-compatible model listing request uses format!("******", api_key) for the Authorization header, which is invalid and prevents authenticating (and may not compile due to the unused format arg). Use bearer auth with the API key.
    packages/ui/src/locales/en.json:35
  • Navigation label uses "Assistant" but the feature and page copy consistently call it "Diagnostic assistant" (e.g., assistant.title). This inconsistency can confuse users and makes it harder to map the sidebar entry to the recovery workspace.
    "overview": "Overview",
    "instances": "Instances",
    "assistant": "Assistant",
    "versions": "Versions",

Comment thread src-tauri/src/core/assistant.rs
Copilot AI review requested due to automatic review settings August 3, 2026 22:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 46 changed files in this pull request and generated no new comments.

Suppressed comments (5)

src-tauri/src/core/assistant.rs:671

  • Same as the non-streaming path: log_context is sanitized but not size/line bounded before being embedded into the system prompt. This can inflate streaming requests and make timeouts/cancellations less effective under large inputs. Cap the context to self.max_log_lines (or a max size) after sanitization.
    src-tauri/src/core/assistant.rs:438
  • log_context is accepted as an arbitrary string and only sanitized, not bounded. A large value can significantly increase memory use and request size/cost to the provider (and could be abused by a compromised frontend). Consider capping the injected context to the assistant's existing max_log_lines (or a max byte/char limit) after sanitization.

This issue also appears on line 668 of the same file.
packages/docs/content/en/manual/features/index.mdx:154

  • This section is now describing the “diagnostic assistant”, but the heading still says “AI Assistant”, which is inconsistent with the rest of the page (and the table row above). Consider renaming the heading to match the feature name used elsewhere.
### AI Assistant
The [diagnostic assistant](/en/docs/manual/features/assistant) provides:

packages/docs/content/zh/manual/features/index.mdx:154

  • 这一段已经改为介绍“诊断助手”,但标题仍为“AI 助手”,与上方表格及后续内容不一致。建议将标题改为“诊断助手”以保持一致性。
### AI 助手
[诊断助手](/docs/manual/features/assistant) 提供:

packages/ui/src/locales/en.json:36

  • The sidebar navigation label uses “Assistant”, while the page title and other UI copy consistently use “Diagnostic assistant”. Consider aligning the nav label to reduce ambiguity.
    "overview": "Overview",
    "instances": "Instances",
    "assistant": "Assistant",
    "versions": "Versions",

Copilot AI review requested due to automatic review settings August 3, 2026 22:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 46 changed files in this pull request and generated no new comments.

Suppressed comments (3)

src-tauri/src/core/assistant.rs:1086

  • assistant_context_deduplicates_after_redaction currently adds the same already-redacted line twice, so it doesn't prove that different secrets collapse to one sanitized line (the key behavioral guarantee for consent-gated evidence). Use two different bearer tokens and assert the sanitized output is the single redacted form.
    src-tauri/src/core/assistant.rs:1075
  • The redaction test uses "Authorization: ******" and then asserts the output still contains ******. That doesn't exercise the Authorization: Bearer <token> redaction path at all, and makes several assertions (e.g. !contains("secret-token")) vacuous. Using a fake bearer token here is safe and ensures the test actually guards against leaking tokens.

This issue also appears on line 1083 of the same file.
src-tauri/src/core/assistant.rs:327

  • get_sanitized_log_context() iterates and deduplicates over all additional_lines before trimming to max_log_lines. If the frontend (or any caller) passes a very large lines array, the HashSet and intermediate Vec can grow unbounded, causing avoidable CPU/memory use. Since log_buffer is already bounded, cap additional_lines up front (e.g., last max_log_lines only) so processing remains bounded.

@HsiangNianian
HsiangNianian merged commit dc50032 into main Aug 3, 2026
27 of 28 checks passed
@HsiangNianian
HsiangNianian deleted the feat/beta1-assistant branch August 3, 2026 22:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FeatureRequest: In-game guide assistant

2 participants