Skip to content

v0.10.0

Choose a tag to compare

@github-actions github-actions released this 13 May 08:18

Changelog

All notable changes to this project will be documented in this file.

v0.10.0 - 2026-05-13

Features

  • Feat: update version to 0.10.0 and enhance changelog with new features and improvements

  • Feat: localise all UI strings for NVDA translator

Add # TRANSLATORS: comments to all translate() and _() calls across:

  • ui/session_state.py — provider labels, status messages, guidance
  • service/error_presentation.py — error titles and messages
  • service/provider_readiness.py — provider display names
  • plugin/controller.py — all script descriptions
  • plugin/application.py — use case titles, provider toggle, layer help
  • plugin/background.py — model preload messages (f-strings → _())
  • plugin/layer_mode.py — layer activation and error messages
  • plugin/presenter.py — empty result message

Add missing WebUI t() keys to Python localized_strings:

  • response_streaming_subtitle, attachment_load_failed, attachments_added_status

Fix WebUI attachment strings to use static fallback + .replace()
instead of template literals for proper localisation support.

  • Feat: improve chat error UX with inline error messages, composer text restoration, and attach-to-current fix

User-facing changes:

  • Add to current chat now injects the summary/description as an assistant message
    into the currently active conversation (was silently opening the window)
  • Provider errors during chat are shown as inline error blocks in the transcript
    instead of a display-mode overlay that hid the conversation
  • The user's message is restored in the composer so they can retry without retyping
  • Error streaming state is properly cleaned up via chat_stream_abort
  • New ErrorBlock content type for styled error rendering in the WebView

Internal changes:

  • StreamProjection.abort(reason) for clean teardown of in-flight streams

  • AttachToCurrentAction carries a token for seed text storage/resolution

  • Error metadata flows through chat_append metadata instead of show_error

  • ErrorPresentation type imported and used for content block construction

  • Full edge-case test coverage for attach-to-current flow

  • Changelog updated for v0.9.3

  • Feat(protocol): add canonical YAML spec and code generator

Replace hand-maintained command/event enums across Python, Rust, and
TypeScript with a single canonical YAML definition
(scripts/protocol.yaml) and a generator script
(scripts/generate_protocol.py).

Generated outputs:

  • addon/.../ui/host_protocol_constants.py (Python string constants)
  • nvda_ui_host/src/protocol_commands.rs (Rust CommandName/EventName
    enums + UiCommand impl with match arms)
  • nvda_ui_host/webui/.../protocol-commands.ts (TS type unions)

Wire protocol.rs to include! the generated Rust enums (506 -> 373
lines). Wire host_protocol.py to import from generated constants with
standalone importlib fallback for tests.

Token savings: 14 commands + 14 events now defined once instead of
across 3 languages. Adding a command = edit protocol.yaml + regenerate.

  • Feat(ui): add AttachToCurrent action for zero-token conversation attachment

Add AttachToCurrentAction that opens chat attached to the current
conversation without requiring a token roundtrip through the
ResultActionStore. This appears alongside OpenChat in result actions,
giving users a lightweight option when they don't need seed text.

  • Feat(config): encrypt API keys at rest using Windows DPAPI

Store Gemini and OpenAI API keys encrypted via Windows DPAPI (crypt32.dll
via ctypes) instead of plaintext in config.yaml. Existing keys are
automatically migrated on next save.

  • New utils/crypto.py with lazy ctypes DPAPI bindings

  • YamlConfigStore transparently encrypts on save, decrypts on load

  • CRYPTPROTECT_UI_FORBIDDEN flag prevents UI hangs on NVDA main thread

  • Sensitive keys detected by ApiKey/ApiToken suffix

  • Feat: update documentation and instructions for TypeScript migration, command handling, and Copilot integration

Fixes

  • Fix(ollama): stop auto-pulling model when not found locally

Change ensureModelInstalled to raise an error instead of
automatically calling ollama pull when the model is missing.
Users must now run 'ollama pull ' manually.

Improvements

  • Docs: add behavioral specs for protocol, stream projection, and presentation intent

Create docs/specs/ as the canonical source of truth for cross-layer
contracts. These specs serve as safety nets for refactoring, onboarding
for coding agents, and code-generation targets.

  • protocol-contract.md: all 14 commands, 14 events, envelope rules
  • stream-projection.md: StreamProjection lifecycle and normalization
  • presentation-intent.md: typed UI intent metadata and builder patterns
  • README.md: spec index and maintenance instructions

Refactoring

  • Refactor(providers): extract shared HTTP utilities from Ollama and OpenAI clients

Create providers/_http_utils.py with three generic helpers that consolidate
nearly identical patterns duplicated across provider API clients:

  • parse_json_response(raw, path, provider) — JSON parsing with descriptive errors
  • read_error_body(error) — HTTPError body extraction
  • request_json_with_retry(make_request, ...) — retry loop with timeout/backoff

Ollama http.py: -83 lines (delegates _parseJSON, _readErrorBody, _requestJSON
to shared utils; keeps _requestPullStream as Ollama-specific).
OpenAI client.py: -46 lines (delegates _parse_json, _read_error_body,
_request_json to shared utils; keeps SSE streaming as OpenAI-specific).

Gemini client.py is not affected — its _request_json pattern differs
(pre-built Request, SSL context, no HTTPError retry, different error
hierarchy).

Net: ~129 lines removed from provider clients, 112 shared lines added.

  • Refactor(plugin): extract ModelCache from UseCasePresenter

Move thread-safe model cache (RLock, {provider -> models} dict, async
refresh with stale-provider guards) into dedicated plugin/model_cache.py.

UseCasePresenter now delegates to ModelCache via _get_cached_models and
_refresh_available_models_async thin wrappers plus an _on_models_cached
callback that syncs UI session state.

Removes threading import and lock boilerplate from presenter.py.

  • Refactor(host): split webview.rs into focused sub-modules

Extract three concerns from the monolithic 636-line webview.rs into
dedicated files included via include!():

  • webview_state.rs (114 lines): static globals, thread-local controller,
    WebViewEvent enum, ready-transition logic, test helpers
  • webview_delivery.rs (123 lines): message send/flush/queue pipeline,
    asset embedding (host.html/css/js)
  • webview_events.rs (61 lines): JS event parsing and dispatch

webview.rs (358 lines) keeps: public API (resize, focus, post_host_command,
notify_window_visible), init_webview, and the test module.

No functional changes. All 5 webview tests pass.

  • Refactor: align legacy EVENT_HOST_CLOSED with canonical close_host spec

Drop the manual EVENT_HOST_CLOSED constant and align all layers
with the spec-driven 'close_host' event name:

  • Rust (window.rs, ipc/state.rs): emit 'close_host' instead of 'host_closed'

  • Python (host_protocol.py): import EVENT_CLOSE_HOST from generated constants

  • Python (host_renderer.py, test_host_protocol.py): EVENT_HOST_CLOSED -> EVENT_CLOSE_HOST

  • TypeScript (bridge.ts): type COMMANDS/CONTROL_COMMANDS with generated CommandName

  • TypeScript (protocol-types.ts): re-export CommandName, EventName, CHAT_COMMANDS

  • Refactor(host): consolidate verbose log messages in app dispatch

Replace multi-line log format strings in handle_command and
handle_raw_message with compact single-line log entries.

  • Shorten log prefix from 'Host app ...' to 'app ...'
  • Merge separate info-level forwarding and policy log lines into one
  • Compress debug-level preview logs to single line
  • Combine activation_policy and focus_request logging

Reduces app.rs from 294 to 272 lines and improves readability of
the command dispatch path without changing behavior.

  • Refactor(ui): extract session TypedDicts to session_types module

Split TypedDict type definitions from session_state.py (318→290 lines)
into a new session_types.py module (56 lines). Consumers that only need
the type shapes can now import from session_types without loading the
builder logic and localized string tables.

  • Move SessionProviderInfo, SessionProviderOption, SessionProviderStatus,
    SessionConversationSummary, UISessionMetadata TypedDicts to new module

  • Keep UISessionState dataclass in session_state.py (has to_metadata method)

  • session_state.py imports types from session_types.py

  • Refactor(ui): extract StreamProjection from adapter to dedicated module

Move _StreamedAssistantProjection and _normalize_stream_fragment from
ui/adapter.py (558→427 lines) into a new ui/stream_projection.py module.

  • Rename _StreamedAssistantProjection → StreamProjection (public API)
  • Rename _normalize_stream_fragment → normalize_stream_fragment
  • Add module docstring referencing spec at docs/specs/stream-projection.md
  • Adapter now imports StreamProjection instead of defining it inline

This reduces the token footprint for agents reading adapter.py by ~130
lines and gives the streaming projection logic a clear, testable home.

  • Refactor(webui): migrate to TypeScript with Svelte 5 clean architecture

Rewrite the WebView UI layer from JavaScript to TypeScript with
a clean architecture that separates concerns and eliminates fragile
reactivity patterns:

  • Extract Transcript class into transcript.svelte.ts with
    for inherent Svelte 5 reactivity, removing dependency on external
    \ proxy wrapping

  • Remove self-referential getter/setter on chat.messages in favor
    of direct appState.chat.transcript.messages access (2 fewer
    proxy hops in the reactivity chain)

  • Split monolithic bridge.js into modular command handlers
    (commands/), operations (operations/), and typed protocol
    types (protocol-types.ts)

  • Add CONTROL_COMMANDS set to bridge.ts so control state
    extraction only runs for open_chat, sync_session, and
    render_display — content-only streaming commands no longer
    trigger wasted updateControlState() calls

  • Replace manual bumpChatRenderVersion() with \ from
    transcript.count for auto-scroll

  • Add JS console.log forwarding to Rust log via webview.rs
    for end-to-end debugging visibility

  • Extract keyboard shortcuts into dedicated shortcuts.ts module

  • Add tsconfig.json and update vite.config.js for TypeScript

  • Add rust-side web_ui_ready safety net in NavigationCompleted

  • Refactor code style to use double quotes consistently, update state management, and add configuration files for biome

SHA256

ff344279bfd86759fd10e4235743abe1c7a50e52000fca8a20cde063f03a56ee AIAssistant-0.10.0.nvda-addon