Skip to content

feat: add StudyOverlay AI tutor demo using Moss semantic memory - #369

Open
AyanBhardwaj1 wants to merge 2 commits into
usemoss:mainfrom
AyanBhardwaj1:add-study-overlay
Open

feat: add StudyOverlay AI tutor demo using Moss semantic memory#369
AyanBhardwaj1 wants to merge 2 commits into
usemoss:mainfrom
AyanBhardwaj1:add-study-overlay

Conversation

@AyanBhardwaj1

@AyanBhardwaj1 AyanBhardwaj1 commented Jul 11, 2026

Copy link
Copy Markdown

Pull Request Checklist

Please ensure that your PR meets the following requirements:

  • I have read the CONTRIBUTING guide.
  • I have updated the documentation (if applicable).
  • My code follows the style guidelines of this project.
  • I have performed a self-review of my own code.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.

Description

Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context.

Fixes # (issue number)

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

@CLAassistant

CLAassistant commented Jul 11, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

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

Adds a new StudyOverlay community demo under moss-live-labs/ showcasing a desktop “always-on-top” study assistant that captures the screen, calls an OpenRouter vision-capable chat model for a tutor-style explanation, and uses Moss as local semantic session memory (JS sidecar by default, Python fallback).

Changes:

  • Introduces a Python + pywebview overlay app with global hotkey capture and a settings UI.
  • Adds Moss “session memory” integration via a Node sidecar (@moss-dev/moss) plus a Python SDK fallback.
  • Adds local settings persistence and demo packaging files for Python and Node dependencies.

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
moss-live-labs/community-demos/study-overlay/settings.py Implements config storage/loading and exposes settings to the UI.
moss-live-labs/community-demos/study-overlay/requirements.txt Python runtime dependencies for the overlay app.
moss-live-labs/community-demos/study-overlay/README.md Demo setup + usage instructions.
moss-live-labs/community-demos/study-overlay/pnpm-lock.yaml Locks Node dependencies for the Moss JS sidecar (and other declared deps).
moss-live-labs/community-demos/study-overlay/package.json Node dependency manifest used to install @moss-dev/moss for the sidecar.
moss-live-labs/community-demos/study-overlay/overlay.js Frontend logic for the overlay UI and settings form.
moss-live-labs/community-demos/study-overlay/overlay.html Overlay UI markup + external script/style includes.
moss-live-labs/community-demos/study-overlay/overlay.css Overlay UI styling.
moss-live-labs/community-demos/study-overlay/moss_sidecar.mjs Node sidecar process that queries/adds docs to Moss.
moss-live-labs/community-demos/study-overlay/memory.py Moss memory abstraction with JS sidecar and Python fallback backends.
moss-live-labs/community-demos/study-overlay/main.py App entrypoint: window creation, hotkey listener, capture flow wiring.
moss-live-labs/community-demos/study-overlay/capture.py Screen capture helper producing PNG/base64/data URL.
moss-live-labs/community-demos/study-overlay/ai.py OpenRouter client + prompt composition for screenshot explanations.
moss-live-labs/community-demos/study-overlay/.gitignore Ignores local build artifacts and local config.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread moss-live-labs/community-demos/study-overlay/overlay.js
Comment on lines +70 to +83
def public_settings(settings: dict[str, str] | None = None) -> dict[str, Any]:
data = settings if settings is not None else load_settings()
return {
"config_path": str(config_path()),
"missing": missing_required(data),
"has_openrouter": bool(data.get("OPENROUTER_API_KEY")),
"has_moss_project_id": bool(data.get("MOSS_PROJECT_ID")),
"has_moss_project_key": bool(data.get("MOSS_PROJECT_KEY")),
"values": {
"OPENROUTER_API_KEY": data.get("OPENROUTER_API_KEY", ""),
"MOSS_PROJECT_ID": data.get("MOSS_PROJECT_ID", ""),
"MOSS_PROJECT_KEY": data.get("MOSS_PROJECT_KEY", ""),
},
}
Comment on lines +163 to +167
deadline = time.time() + timeout
while time.time() < deadline:
line = self._process.stdout.readline()
if not line:
if self._process.poll() is not None:
Comment on lines +8 to +12
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.17.0/dist/katex.min.css" crossorigin="anonymous" />
<script defer src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.17.0/dist/katex.min.js" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.17.0/dist/contrib/auto-render.min.js" crossorigin="anonymous"></script>
<script defer src="overlay.js"></script>
@@ -0,0 +1,27 @@
{
"name": "moss-meeting-copilot-overlay",
Comment on lines +10 to +14
"dependencies": {
"@moss-dev/moss": "^1.0.0",
"next": "^15.3.0",
"react": "^19.0.0",
"react-dom": "^19.0.0"
Comment on lines +28 to +32
def __init__(self) -> None:
self.window: webview.Window | None = None
self.memory = StudyMemory()
self.busy = False

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@AyanBhardwaj1

Copy link
Copy Markdown
Author

Committed changes from Copilot, please merge.

<title>StudyOverlay</title>
<link rel="stylesheet" href="overlay.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.17.0/dist/katex.min.css" crossorigin="anonymous" />
<script defer src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

BLOCKING ```html

<script defer src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
This privileged pywebview page exposes `window.pywebview.api`, including screen capture and settings APIs, but it executes remote CDN scripts. A CDN compromise or unpinned `marked` update can call `run_capture()` or read settings from the renderer. Bundle/vendor these assets locally and load only `self` scripts, ideally with a CSP; avoid giving third-party code access to the pywebview bridge.

"has_moss_project_id": bool(data.get("MOSS_PROJECT_ID")),
"has_moss_project_key": bool(data.get("MOSS_PROJECT_KEY")),
"values": {
"OPENROUTER_API_KEY": data.get("OPENROUTER_API_KEY", ""),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

BLOCKING ```py
"OPENROUTER_API_KEY": data.get("OPENROUTER_API_KEY", ""),

`public_settings()` sends raw API keys back into the webview, so any renderer XSS or third-party script can exfiltrate OpenRouter and Moss credentials. Return only booleans/masked labels, leave password fields blank on edit, and make save semantics preserve existing secrets unless the user supplies a replacement or explicitly clears them.

const source = markdown || "";
const escaped = source.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
if (window.marked) {
responseEl.innerHTML = window.marked.parse(escaped, { mangle: false, headerIds: false });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

BLOCKING ```js
responseEl.innerHTML = window.marked.parse(escaped, { mangle: false, headerIds: false });

The model response is untrusted input, and rendering Marked output directly into `innerHTML` leaves HTML/URL injection paths in the same privileged renderer that can reach `window.pywebview.api`. Sanitize the generated HTML with DOMPurify or render a safe Markdown subset, and reject dangerous link/image protocols before assigning to `innerHTML`.


deadline = time.time() + timeout
while time.time() < deadline:
line = self._process.stdout.readline()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

CONSIDER ```py
line = self._process.stdout.readline()

This blocking `readline()` means the surrounding deadline is ineffective: if the sidecar hangs without emitting a newline, startup or capture can wait forever instead of disabling memory. Read stdout on a background thread into a `Queue` and use `queue.get(timeout=...)`, or use nonblocking I/O/select; on timeout, kill/restart the sidecar and fall back to no memory.

self.window.show()

def run_capture(self, user_prompt: str = "") -> dict[str, Any]:
if self.busy:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

CONSIDER ```py
if self.busy:
return {"ok": False, "error": "StudyOverlay is already working on a capture."}

`busy` is a plain check-then-set flag shared by pywebview calls and hotkey worker threads, so two near-simultaneous captures can both pass the check and run, racing window hide/show and Moss sidecar access. Replace it with a `threading.Lock` or `Semaphore(1)` and acquire non-blocking around the whole capture flow.

@github-actions

Copy link
Copy Markdown

Codex review

The PR adds a functional desktop overlay demo, but the renderer/security boundary is too loose for an app that captures the screen and stores API credentials. I also found a capture concurrency race and a sidecar timeout that can hang instead of falling back cleanly.

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.

4 participants