Skip to content

RinaAssistant v3.0.0 — Large Update & Fixes

Choose a tag to compare

@Luna-coreX Luna-coreX released this 30 Aug 02:17
· 367 commits to main since this release

Changelog

All notable changes to Rina Assistant.
Releases before 3.0.0 are documented on the releases page.


3.0.0 — 2026-08-30

The largest release so far. Rina's brain was pulled out of the interface and now runs on its own; free-form questions can be answered by a local language model; and a full security and crash audit closed a set of issues, two of which could destroy data or run someone else's code.

Highlights

  • Headless core. All command handling moved into core/, which imports no Qt at all. The window is now a thin shell subscribed to events.
  • AI answers. Anything Rina can't parse can be answered by a local Ollama model instead of "Sorry, I didn't understand."
  • Security audit. Two critical plugin vulnerabilities fixed, plus untrusted text no longer rendered as markup, and imported commands can no longer arrive pre-armed.
  • Crash audit. ~600 hostile inputs, corrupted data files and concurrent access: 30 failures found, 0 remaining.

Architecture: the headless core

Rina used to think inside voice/service.py, which was a QObject. Recognition, parsing, launching and speaking were all entangled with the widget tree, so none of it could run — or be tested — without a window on screen.

  • core/engine.py — the whole pipeline: pending questions → plugins → user commands → reminders → system control → app launcher → built-ins → language model → web fallback. Zero Qt imports.
  • core/events.py — a small synchronous event bus. The core announces what happened; it never touches a widget.
  • core/protocol.py — every event and its payload declared in one place, with a PROTOCOL_VERSION. Payloads are plain JSON-serialisable values, so this contract becomes an IPC protocol unchanged when the shell moves to another process or another language.
  • voice/service.py — reduced from ~700 lines of logic to a Qt adapter that forwards core events to signals. −538 lines.

Practical consequences today: the core can be driven headless in tests, the shell can be replaced without touching behaviour, and a background thread can no longer accidentally call into Qt.


AI answers via Ollama

When no handler claims a phrase, Rina can now ask a local model.

  • Local by design. Requests go only to the address in settings (localhost:11434 by default). Nothing leaves the machine; if you point it elsewhere, the settings page says so plainly.
  • No new dependencies. Ollama speaks HTTP, so urllib is enough.
  • Conversational. The last 6 exchanges are sent as context, so follow-up questions work.
  • Spoken-answer persona. The default prompt asks for one or two sentences, no lists, no markup, in the speaker's language — the answer is read aloud.
  • New THINKING event. The interface shows an indicator instead of going silent for several seconds.
  • Settings → AI. Enable, server address, model picker (populated from the server), custom persona, timeout, and a "Check connection" button.

Disabled by default. With it off, the pipeline behaves exactly as in 2.5.0.


Security

Every issue below was reproduced in a sandbox before being fixed, not inferred from reading the code.

Critical

Plugin installation could delete the application. A plugin.json declaring "id": ".." escaped the plugins folder, and the overwrite step then removed the application root — main.py, core/, every plugin. Fixed in three layers: plugin ids are validated against a strict whitelist, the resulting path is re-checked after joining to confirm it is a direct child of the plugins folder, and nothing is removed unless the target actually contains a plugin.json.

A plugin could run without being enabled. An archive reusing the id of an already-enabled plugin replaced it, and its code executed immediately during the automatic re-scan — while the interface still said "Enable it below." Replacing a plugin now forces it back to disabled, and the confirmation message says a plugin was replaced.

Folder names are now authoritative. discover() used to trust the id inside the manifest, so a manifest could claim another plugin's identity and inherit its stored settings. The folder name wins.

High

Untrusted text is no longer rendered as markup. Qt labels interpret HTML by default and setTextFormat was never called, so a model reply, a recognised phrase, plugin output or a plugin manifest containing <img src="file://some-host/x"> would make Windows reach out to that host over SMB. All externally-sourced text — history, toasts, plugin pages, plugin cards — is now explicitly plain text.

Imported commands can no longer arrive armed. Command files were validated only by their envelope; type, target and enabled were trusted verbatim, so a shared file could plant an enabled command pointing at an arbitrary executable or UNC path. Imported entries are now reduced to known fields, checked against the known command and system-action types, capped (500 commands, 20 triggers, 200 characters each, minimum 2), and always imported disabled.

System binaries are launched by absolute path. shutdown.exe and rundll32.exe were invoked by bare name, and Windows' search order includes the current directory — a file dropped there would run instead. Resolved through %SystemRoot%\System32 now.

Medium

  • The Ollama address was accepted as free text; it is now required to be http/https with a host, responses are capped at 4 MB, and a non-local address is flagged in settings.
  • Removed a dead dispatch_user_command path that skipped the confirmation prompt for destructive actions.

Reliability

Settings could be lost, and could kill the reminder scheduler. save() iterated the live dirty-key set while another thread modified it, raising RuntimeError: Set changed size during iteration. The store is now guarded by a lock and snapshots the set before writing.

Vosk speech recognition never worked. A missing import os made the model loader raise NameError on the first phrase for anyone who installed Vosk.

Corrupted data no longer breaks a whole tab. History and reminder stores now normalise and drop malformed records instead of propagating them into the interface.

Absurd dates are no longer permanent. "Remind me in 99999999999999999999 minutes" produced an entry that broke the Reminders tab on every launch and could not be deleted. Durations are now bounded, dates are clamped to ten years out, and unformattable timestamps degrade to —.

Duplicate command execution. Toggling always-listen twice left two microphone threads running, so a single phrase was handled twice. Each run now owns its own stop signal.

Interface freezes. The full application scan — including a PowerShell call for Store apps — ran on the interface thread when opening the command editor. It now reads the cached index and refreshes in the background.

Screenshots crashed from the wrong thread. Screen capture is a Qt operation and was being called from the worker; it now travels to the shell as a window action and is taken on the interface thread.

Also fixed: a failed system action reported success inside sequences; a malformed format field escaped the import handler as an unhandled ValueError; the "Refresh applications" button could stay disabled after a failed scan.


Interface

  • New AI section in Settings with a live connection check.
  • History messages measure their own wrapped height, so long replies are no longer clipped.
  • Plugin cards and plugin-authored pages render text safely (see above).
  • Documentation strings corrected where they had drifted from the code (event contract, reminder scheduler).

Housekeeping

  • Removed the empty ui/ package left over from 1.0.0; the build spec now ships core/.
  • README rewritten from scratch for the current application, with screenshots.

Verification

  • Crash test: ~600 hostile inputs (empty, 10 000 characters, emoji, control and RTL characters, ../.., UNC paths, division by zero, 2**2**2**2**2, "at 99:99"), corrupted data files, answers with no question pending, and 4 concurrent threads — 30 failures → 0.
  • Security probes: path traversal 0 failures; plugin replacement no longer executes on discovery.
  • Regression: command pipeline, reminders, plugins, import/export, all 5 languages, all 5 themes — 0 failures.
  • Translations: 0 untranslated strings.

Known limitations

  • Typed commands still run the pipeline on the interface thread; the worst stall (the application scan) is gone, but launching still happens there.
  • Overlapping speech replies share one busy flag, and gTTS/Piper use fixed temporary filenames — two answers in quick succession can interfere.
  • History, reminders and usage statistics still use read-modify-write updates.
  • "1 hour 30 minutes" is parsed as one hour; only the first unit is read.