Skip to content

feat: support to build for windows and add proxy when do http request…#495

Closed
peilinok wants to merge 1 commit intoRunanywhereAI:mainfrom
peilinok:syLar/win
Closed

feat: support to build for windows and add proxy when do http request…#495
peilinok wants to merge 1 commit intoRunanywhereAI:mainfrom
peilinok:syLar/win

Conversation

@peilinok
Copy link
Copy Markdown

@peilinok peilinok commented Apr 23, 2026

… or download modals

Description

Brief description of the changes made.

Type of Change

  • Bug fix
  • New feature
  • Documentation update
  • Refactoring

Testing

  • Lint passes locally
  • Added/updated tests for changes

Platform-Specific Testing (check all that apply)

Swift SDK / iOS Sample:

  • Tested on iPhone (Simulator or Device)
  • Tested on iPad / Tablet
  • Tested on Mac (macOS target)

Kotlin SDK / Android Sample:

  • Tested on Android Phone (Emulator or Device)
  • Tested on Android Tablet

Flutter SDK / Flutter Sample:

  • Tested on iOS
  • Tested on Android

React Native SDK / React Native Sample:

  • Tested on iOS
  • Tested on Android

Playground:

  • Tested on target platform
  • Verified no regressions in existing Playground projects
    Web SDK / Web Sample:
  • Tested in Chrome (Desktop)
  • Tested in Firefox
  • Tested in Safari
  • WASM backends load (LlamaCpp + ONNX)
  • OPFS storage persistence verified (survives page refresh)
  • Settings persistence verified (localStorage)

Labels

Please add the appropriate label(s):

SDKs:

  • Swift SDK - Changes to Swift SDK (sdk/runanywhere-swift)
  • Kotlin SDK - Changes to Kotlin SDK (sdk/runanywhere-kotlin)
  • Flutter SDK - Changes to Flutter SDK (sdk/runanywhere-flutter)
  • React Native SDK - Changes to React Native SDK (sdk/runanywhere-react-native)
  • Web SDK - Changes to Web SDK (sdk/runanywhere-web)
  • Commons - Changes to shared native code (sdk/runanywhere-commons)

Sample Apps:

  • iOS Sample - Changes to iOS example app (examples/ios)
  • Android Sample - Changes to Android example app (examples/android)
  • Flutter Sample - Changes to Flutter example app (examples/flutter)
  • React Native Sample - Changes to React Native example app (examples/react-native)
  • Web Sample - Changes to Web example app (examples/web)

Checklist

  • Code follows project style guidelines
  • Self-review completed
  • Documentation updated (if needed)

Screenshots

Attach relevant UI screenshots for changes (if applicable):

  • Mobile (Phone)
  • Tablet / iPad
  • Desktop / Mac

Summary by CodeRabbit

  • New Features

    • Windows support (preview) with native backend compilation
    • HTTP proxy configuration for general requests and model downloads
    • Enhanced Vision feature with camera integration and platform capability detection
    • Improved model file resolution for multimodal AI models
  • Bug Fixes

    • Vision camera initialization and error handling improvements
    • Cross-platform model path resolution for auxiliary files
  • Documentation

    • Windows-specific build and setup instructions added
    • Updated platform support matrix

Note

Medium Risk
Medium risk because it adds a new Windows build target (new CMake/runner files and native DLL staging) and introduces proxy routing for HTTP/download traffic, which can affect networking and model download reliability across platforms.

Overview
Adds Windows support for the Flutter RunAnywhereAI example, including a new windows/ runner project, camera_windows integration, and updated docs/scripts for building native backends (including Sherpa-ONNX dependency download/staging) on Windows.

Introduces scoped proxy support (general vs download): new proxy settings models + persistence (SharedPreferences + secure storage), a settings UI to configure/test/clear proxies, and wiring so SDK model downloads use a proxy-aware HTTP client factory.

Refactors/adjusts example app behavior for Windows constraints: capability gating + UnsupportedFeatureView for unavailable features (notably RAG on Windows), a camera backend abstraction for Vision (to support Windows camera plugin), and small fixes/compat tweaks (dropdown value usage, plugin registrant updates, error propagation, and minor theming/logging changes).

Reviewed by Cursor Bugbot for commit c29f567. Configure here.

Copilot AI review requested due to automatic review settings April 23, 2026 02:33
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 23, 2026

Too many files changed for review. (125 files found, 100 file limit)

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 23, 2026

📝 Walkthrough

Walkthrough

This PR adds comprehensive Windows desktop support to the RunAnywhereAI Flutter application and SDK, introducing a new Windows build pipeline (CMake-based native runner and plugins), a flexible proxy configuration system with dual scopes, vision/VLM camera abstraction, and platform capability gating to conditionally enable features per platform.

Changes

Cohort / File(s) Summary
Windows Platform Build Support
examples/flutter/RunAnywhereAI/windows/CMakeLists.txt, windows/flutter/CMakeLists.txt, windows/flutter/generated_plugin_registrant.*, windows/flutter/generated_plugins.cmake, windows/runner/*
Introduces complete Windows CMake build system, Flutter runner executable (main.cpp, flutter_window.cpp, win32_window.cpp), and platform-layer utilities for console/argument handling, Windows resource script, and manifest configuration.
Flutter Plugin Registration (Windows)
examples/flutter/RunAnywhereAI/ios/Runner/GeneratedPluginRegistrant.m, android/app/src/main/java/.../GeneratedPluginRegistrant.java, sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin.*, sdk/.../runanywhere_llamacpp/windows/runanywhere_*_plugin.*, sdk/.../runanywhere_onnx/windows/runanywhere_*_plugin.*
Updates generated plugin registration for iOS (record/sqflite imports), Android (sqflite logging), and introduces new Windows C++ Flutter plugin implementations with method channel handlers for version/capability reporting.
Proxy Configuration System
examples/flutter/RunAnywhereAI/lib/core/models/proxy_settings.dart, lib/core/services/proxy_settings_service.dart, lib/core/services/example_http_service.dart, lib/core/utilities/constants.dart, lib/features/settings/combined_settings_view.dart
Adds ProxyScope/ProxyScheme enums, ProxySettings model with validation, ProxySettingsService for persistence (SharedPreferences + Keychain), ExampleHttpService providing HTTP operations with proxy scoping, and comprehensive settings UI with configure/test/clear actions per scope.
Vision/VLM Camera Abstraction
examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dart, lib/features/vision/services/vision_permission_gateway.dart, lib/features/vision/services/vision_vlm_service.dart, lib/features/vision/vlm_view_model.dart, lib/features/vision/vlm_camera_view.dart, lib/features/vision/vision_hub_view.dart
Introduces abstraction interfaces (VisionCameraBackend, VisionCameraSession, VisionVlmService, VisionPermissionGateway) with camera-plugin implementation, refactors VLM view-model to use injected services, updates camera preview UI, and adds capability gating.
Platform Capability Gating
examples/flutter/RunAnywhereAI/lib/core/services/platform_capability_service.dart, lib/features/chat/chat_interface_view.dart, lib/features/rag/rag_demo_view.dart, lib/features/vision/vision_hub_view.dart, lib/features/voice/speech_to_text_view.dart, lib/features/voice/text_to_speech_view.dart, lib/features/voice/voice_assistant_view.dart, lib/core/design_system/unsupported_feature_view.dart
Adds PlatformCapabilityService singleton gating chat/tools/vision/speech/RAG features (with Windows-specific RAG disable), integrates UnsupportedFeatureView widget, and gates feature views with runtime capability checks.
SDK & Core Changes
sdk/runanywhere-commons/CMakeLists.txt, sdk/.../backends/llamacpp/CMakeLists.txt, sdk/.../backends/onnx/CMakeLists.txt, sdk/.../src/core/rac_core.cpp, sdk/.../src/infrastructure/download/download_orchestrator.cpp, sdk/.../src/infrastructure/extraction/rac_extraction_stub.cpp
Introduces RAC_BUILD_VLM CMake option, Windows-specific backend compiler settings, diffusion-model registry guard (iOS/macOS only), archive-extraction stub for Windows, download orchestrator model-file filtering (mmproj exclusion) and directory-existence checks.
SDK Download & Auth
sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart, sdk/.../lib/internal/vlm_file_resolution.dart, sdk/.../lib/native/dart_bridge_auth.dart, sdk/.../lib/native/dart_bridge_stt.dart, sdk/.../lib/native/dart_bridge_tts.dart, sdk/.../lib/native/platform_loader.dart, sdk/.../lib/native/dart_bridge_model_registry.dart, sdk/.../lib/public/runanywhere.dart
Adds injectable DownloadHttpClientFactory for model downloads, VLM model path/mmproj resolution helpers, Windows DLL loader improvements, Isolate-based FFI snapshots for STT/TTS, auth struct layout updates, and public configureDownloadHttpClientFactory API.
Windows Build Scripts & Documentation
sdk/runanywhere-commons/scripts/build-windows.bat, sdk/.../scripts/windows/download-sherpa-onnx.bat, sdk/runanywhere-commons/README.md, docs/building.md, sdk/runanywhere-flutter/packages/runanywhere/README.md
Simplifies Windows x64 build script (DLL-only output), updates Sherpa-ONNX Windows download/extraction with normalized layout, and adds Windows-specific build/validation documentation.
UI & Model Updates
examples/flutter/RunAnywhereAI/lib/core/design_system/app_colors.dart, lib/features/models/model_list_view_model.dart, lib/features/models/model_selection_sheet.dart, lib/features/models/add_model_from_url_view.dart, lib/features/structured_output/structured_output_view.dart, lib/app/runanywhere_ai_app.dart, pubspec.yaml
Adds ColorCompatibility extension for optional alpha, gates System TTS on Windows, switches dropdowns to controlled value binding, routes downloads through custom HTTP client factory, updates plugin versions/dependencies, and reformats model URLs.
Test Suite
test/features/vision/vlm_view_model_test.dart, test/widget_test.dart, test/core/models/proxy_settings_test.dart, test/core/services/example_http_service_test.dart, test/core/services/proxy_settings_service_test.dart, test/features/dropdown_form_field_compatibility_test.dart, sdk/.../test/vlm_file_resolution_test.dart, sdk/.../test/dart_bridge_auth_test.dart, sdk/.../test/download_service_test.dart, sdk/.../test/auth_sdk_config_ffi_layout_test.dart
Adds comprehensive test coverage for VLM view-model with dependency injection, proxy settings validation/persistence, HTTP service proxy directive, vision file resolution, auth FFI layout, download progress calculation, and UI widget interactions.
Miscellaneous
.gitignore, examples/flutter/RunAnywhereAI/.gitignore, examples/flutter/RunAnywhereAI/windows/.gitignore, sdk/runanywhere-flutter/packages/runanywhere/pubspec.yaml, sdk/.../runanywhere_llamacpp/pubspec.yaml, sdk/.../runanywhere_onnx/pubspec.yaml
Updates .gitignore patterns for worktrees and Windows artifacts, adds Windows platform plugin registrations in pubspec files.

Sequence Diagram(s)

sequenceDiagram
    participant App as Flutter App
    participant ProxySvc as ProxySettingsService
    participant ExHttpSvc as ExampleHttpService
    participant HttpClient as HttpClient
    participant Network as Network/API

    App->>ProxySvc: load(ProxyScope)
    ProxySvc->>ProxySvc: read SharedPrefs + Keychain
    ProxySvc-->>App: ProxySettings

    App->>App: user configures proxy
    App->>ProxySvc: save(scope, settings)
    ProxySvc->>ProxySvc: validate(settings)
    ProxySvc->>ProxySvc: persist to SharedPrefs + Keychain
    ProxySvc-->>App: validation result

    App->>ExHttpSvc: testProxy(scope, uri)
    ExHttpSvc->>ProxySvc: load(scope)
    ProxySvc-->>ExHttpSvc: ProxySettings
    ExHttpSvc->>HttpClient: createScopedHttpClient(scope)
    HttpClient->>HttpClient: configure with ProxySettings
    HttpClient->>Network: HTTP GET request
    Network-->>HttpClient: 2xx response
    HttpClient-->>ExHttpSvc: success
    ExHttpSvc-->>App: test result
Loading
sequenceDiagram
    participant VLMView as VLMView
    participant VLMViewModel as VLMViewModel
    participant CameraBackend as VisionCameraBackend
    participant CameraSession as VisionCameraSession
    participant VLMService as VisionVlmService
    participant SDK as RunAnywhere SDK

    VLMView->>VLMViewModel: init with injected backends
    VLMViewModel->>CameraBackend: listDevices()
    CameraBackend-->>VLMViewModel: List<VisionCameraDevice>
    VLMViewModel->>CameraBackend: createSession(device)
    CameraBackend-->>VLMViewModel: VisionCameraSession
    VLMViewModel->>CameraSession: initialize()
    CameraSession-->>VLMViewModel: initialized

    VLMView->>VLMViewModel: captureAndDescribeImage()
    VLMViewModel->>CameraSession: captureStill()
    CameraSession-->>VLMViewModel: imagePath
    VLMViewModel->>VLMService: processImageStream(imagePath, prompt)
    VLMService->>SDK: loadModel(modelId)
    SDK-->>VLMService: model loaded
    VLMService->>SDK: processImageStream(VLMImage, options)
    SDK-->>VLMService: Stream<String> tokens
    VLMService-->>VLMViewModel: Stream<String>
    VLMViewModel->>VLMViewModel: yield tokens, update description
    VLMViewModel-->>VLMView: currentDescription
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested labels

flutter-sdk, flutter-sample, windows-support, enhancement, proxy-configuration, vision-llm

Suggested reviewers

  • shubhammalhotra28
  • sanchitmonga22
  • Siddhesh2377

Poem

🐰 A Windows tale of hops and hopes!

Across the platforms, Flutter hops,
With proxies configured at each stop,
Vision cameras see the light,
While Windows runners run just right—
A feature-gated, tested sight! 🪟✨

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 adds an initial Windows “vertical slice” for the Flutter SDK and example app, and introduces configurable proxy support for HTTP model downloads.

Changes:

  • Add Windows plugin implementations/CMake bundling for runanywhere, runanywhere_llamacpp, and runanywhere_onnx.
  • Add proxy-configurable HTTP client factory for model downloads (SDK) plus example app proxy services/settings.
  • Update runanywhere-commons for Windows build workflows (MSVC build script, filesystem fixes, model-path selection, ONNX/llama.cpp Windows tweaks) and add Windows app runner scaffolding.

Reviewed changes

Copilot reviewed 88 out of 125 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin_c_api.cpp Adds Windows C API registration shim for the ONNX Flutter plugin.
sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin.h Declares Windows plugin class for ONNX method channel.
sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin.cpp Implements basic Windows method-channel handlers for ONNX plugin metadata.
sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/include/runanywhere_onnx/runanywhere_onnx_plugin_c_api.h Exposes C API header for Windows registration.
sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/CMakeLists.txt Builds plugin DLL and stages ONNX runtime DLL dependencies when available.
sdk/runanywhere-flutter/packages/runanywhere_onnx/pubspec.yaml Registers Windows platform for ONNX Flutter plugin.
sdk/runanywhere-flutter/packages/runanywhere_onnx/lib/native/onnx_bindings.dart Loads rac_backend_onnx on Windows via PlatformLoader.
sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin_c_api.cpp Adds Windows C API registration shim for llama.cpp Flutter plugin.
sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.h Declares Windows plugin class for llama.cpp method channel.
sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.cpp Implements Windows method-channel handlers for llama.cpp plugin metadata.
sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/include/runanywhere_llamacpp/runanywhere_llamacpp_plugin_c_api.h Exposes C API header for Windows registration.
sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/CMakeLists.txt Builds plugin DLL and requires/stages rac_backend_llamacpp.dll.
sdk/runanywhere-flutter/packages/runanywhere_llamacpp/pubspec.yaml Registers Windows platform for llama.cpp Flutter plugin.
sdk/runanywhere-flutter/packages/runanywhere_llamacpp/lib/native/llamacpp_bindings.dart Loads rac_backend_llamacpp on Windows via PlatformLoader.
sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin_c_api.cpp Adds Windows C API registration shim for core RunAnywhere plugin.
sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin.h Declares Windows plugin class for core RunAnywhere method channel.
sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin.cpp Implements Windows method-channel handlers for SDK/commons version metadata.
sdk/runanywhere-flutter/packages/runanywhere/windows/include/runanywhere/runanywhere_plugin_c_api.h Exposes C API header for Windows registration.
sdk/runanywhere-flutter/packages/runanywhere/windows/CMakeLists.txt Builds plugin DLL and requires/stages rac_commons.dll.
sdk/runanywhere-flutter/packages/runanywhere/test/vlm_file_resolution_test.dart Adds tests for Windows-safe basename and VLM file resolution.
sdk/runanywhere-flutter/packages/runanywhere/pubspec.yaml Registers Windows platform for core RunAnywhere Flutter plugin.
sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart Exposes configureDownloadHttpClientFactory and routes VLM path resolution via helper.
sdk/runanywhere-flutter/packages/runanywhere/lib/native/platform_loader.dart Improves Windows DLL lookup by checking the executable directory first.
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_tts.dart Avoids accessing handle fields directly inside Isolate.run closure.
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_stt.dart Avoids accessing handle fields directly inside Isolate.run closure.
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_model_registry.dart Normalizes Windows path separators when listing directory entries.
sdk/runanywhere-flutter/packages/runanywhere/lib/internal/vlm_file_resolution.dart Adds Windows-safe VLM file resolution helpers.
sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart Adds injectable HTTP client factory for downloads (proxy support) and per-file client lifecycle.
sdk/runanywhere-flutter/packages/runanywhere/README.md Documents Windows setup steps and marks Windows support as preview.
sdk/runanywhere-commons/tests/test_download_orchestrator.cpp Adds test to ensure model resolution prefers primary .gguf over mmproj*.gguf.
sdk/runanywhere-commons/src/infrastructure/extraction/rac_extraction_stub.cpp Adds a stub extraction implementation for builds without native extraction.
sdk/runanywhere-commons/src/infrastructure/download/download_orchestrator.cpp Adds Windows std::filesystem paths/dir listing, skips auxiliary model files, and improves nested-directory selection logic.
sdk/runanywhere-commons/src/core/rac_core.cpp Restricts diffusion registry initialization to iOS/macOS (not “non-Android”).
sdk/runanywhere-commons/src/backends/onnx/rac_backend_onnx_register.cpp Adds Windows-friendly directory handling in can_handle and compile-time gating for embeddings registration.
sdk/runanywhere-commons/src/backends/onnx/onnx_backend.cpp Avoids dirent.d_type reliance by using stat for directory diagnostics.
sdk/runanywhere-commons/src/backends/onnx/CMakeLists.txt Introduces RAC_ONNX_EMBEDDINGS_AVAILABLE define and Windows build messaging.
sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt Adds Windows build configuration and toggles VLM mtmd based on RAC_BUILD_VLM.
sdk/runanywhere-commons/scripts/windows/download-sherpa-onnx.bat Makes downloads/extraction more robust; normalizes Sherpa DLL placement and verifies expected artifacts.
sdk/runanywhere-commons/scripts/build-windows.bat Replaces prior Windows build script with simplified backend selection + DLL staging flow.
sdk/runanywhere-commons/README.md Documents Windows build usage and Windows ONNX dependency flow.
sdk/runanywhere-commons/CMakeLists.txt Adds RAC_BUILD_VLM option for controlling llama.cpp multimodal build.
examples/flutter/RunAnywhereAI/windows/runner/win32_window.h Adds Windows runner window abstraction (Flutter template).
examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp Implements Win32 window plumbing + dark mode support (Flutter template).
examples/flutter/RunAnywhereAI/windows/runner/utils.h Adds runner console/util declarations (Flutter template).
examples/flutter/RunAnywhereAI/windows/runner/utils.cpp Implements console attach/create and arg conversion (Flutter template).
examples/flutter/RunAnywhereAI/windows/runner/runner.exe.manifest Adds DPI awareness + supported OS manifest (Flutter template).
examples/flutter/RunAnywhereAI/windows/runner/resource.h Adds Windows resource IDs (Flutter template).
examples/flutter/RunAnywhereAI/windows/runner/main.cpp Adds Windows runner entry point + COM init (Flutter template).
examples/flutter/RunAnywhereAI/windows/runner/flutter_window.h Adds Flutter-hosting window wrapper (Flutter template).
examples/flutter/RunAnywhereAI/windows/runner/flutter_window.cpp Implements Flutter view controller hosting + message handling (Flutter template).
examples/flutter/RunAnywhereAI/windows/runner/Runner.rc Adds Windows resources/version info (Flutter template).
examples/flutter/RunAnywhereAI/windows/runner/CMakeLists.txt Adds runner build target, resources, and dependencies (Flutter template).
examples/flutter/RunAnywhereAI/windows/flutter/generated_plugins.cmake Generated plugin list for Windows build (includes RunAnywhere plugins).
examples/flutter/RunAnywhereAI/windows/flutter/generated_plugin_registrant.h Generated plugin registrant header for Windows.
examples/flutter/RunAnywhereAI/windows/flutter/generated_plugin_registrant.cc Generated plugin registrant implementation for Windows.
examples/flutter/RunAnywhereAI/windows/flutter/CMakeLists.txt Flutter-managed Windows build rules (template).
examples/flutter/RunAnywhereAI/windows/CMakeLists.txt Project-level Windows build and install rules (template).
examples/flutter/RunAnywhereAI/windows/.gitignore Adds Windows-specific ignore rules for example app.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/include/flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h Vendored Windows secure storage plugin headers.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/flutter_secure_storage_windows_plugin.h Vendored Windows secure storage plugin headers (duplicate include path).
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/CMakeLists.txt Vendored Windows secure storage plugin build rules.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/pubspec.yaml Adds vendored federated Windows implementation package metadata.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_stub.dart Adds non-Windows stub implementation for vendored plugin.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart Adds Windows DPAPI-backed implementation (vendored).
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/flutter_secure_storage_windows.dart Adds conditional export for stub vs FFI implementation.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/win32_window.h Vendored plugin example Windows runner files.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/win32_window.cpp Vendored plugin example Windows runner files.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/utils.h Vendored plugin example Windows runner files.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/utils.cpp Vendored plugin example Windows runner files.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/runner.exe.manifest Vendored plugin example Windows runner files.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/resource.h Vendored plugin example Windows runner files.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/main.cpp Vendored plugin example Windows runner files.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/flutter_window.h Vendored plugin example Windows runner files.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/flutter_window.cpp Vendored plugin example Windows runner files.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/Runner.rc Vendored plugin example Windows runner files.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/CMakeLists.txt Vendored plugin example Windows runner build rules.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/flutter/generated_plugins.cmake Vendored plugin example generated plugin listing.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/flutter/generated_plugin_registrant.h Vendored plugin example registrant header.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/flutter/generated_plugin_registrant.cc Vendored plugin example registrant implementation.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/flutter/CMakeLists.txt Vendored plugin example Flutter-managed Windows build rules.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/CMakeLists.txt Vendored plugin example project-level Windows build rules.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/pubspec.yaml Vendored plugin example pubspec.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/lib/main.dart Vendored plugin example app.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/analysis_options.yaml Vendored plugin example analyzer config.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/README.md Vendored plugin example readme.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/analysis_options.yaml Vendored plugin analyzer config.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/README.md Vendored plugin readme.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/LICENSE Vendored plugin license (BSD-3).
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/CHANGELOG.md Vendored plugin changelog.
examples/flutter/RunAnywhereAI/test/widget_test.dart Avoids pumpAndSettle timeouts by using bounded pump calls.
examples/flutter/RunAnywhereAI/test/features/vision/vlm_view_model_test.dart Adds VLM view model tests with fake camera + VLM service abstractions.
examples/flutter/RunAnywhereAI/pubspec.yaml Pins Windows camera implementation and vendors secure storage Windows plugin; adjusts record version.
examples/flutter/RunAnywhereAI/lib/features/voice/voice_assistant_view.dart Gates voice assistant UI on platform capability service.
examples/flutter/RunAnywhereAI/lib/features/voice/text_to_speech_view.dart Updates model state handling + gates TTS UI on platform capability service.
examples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dart Updates model state refresh + gates STT UI on platform capability service.
examples/flutter/RunAnywhereAI/lib/features/vision/vlm_camera_view.dart Switches to camera session abstraction and improves error presentation and permission CTA.
examples/flutter/RunAnywhereAI/lib/features/vision/vision_hub_view.dart Gates Vision hub UI on platform capability service.
examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_vlm_service.dart Introduces VLM service abstraction + RunAnywhere-backed implementation.
examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_permission_gateway.dart Introduces permission gateway abstraction for camera permissions.
examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dart Introduces camera backend/session abstractions, wrapping camera plugin.
examples/flutter/RunAnywhereAI/lib/features/settings/tool_settings_view_model.dart Routes example HTTP calls through proxy-aware ExampleHttpService.
examples/flutter/RunAnywhereAI/lib/features/rag/rag_demo_view.dart Gates RAG demo UI on platform capability service.
examples/flutter/RunAnywhereAI/lib/features/models/model_selection_sheet.dart Hides System TTS option on Windows.
examples/flutter/RunAnywhereAI/lib/features/models/model_list_view_model.dart Rethrows model-load errors after notifying listeners.
examples/flutter/RunAnywhereAI/lib/features/chat/chat_interface_view.dart Prevents navigation to unsupported RAG feature on Windows; minor logging/format changes.
examples/flutter/RunAnywhereAI/lib/core/utilities/constants.dart Adds proxy preference/keychain key constants.
examples/flutter/RunAnywhereAI/lib/core/services/proxy_settings_service.dart Adds persisted proxy settings service with validation + legacy migration path.
examples/flutter/RunAnywhereAI/lib/core/services/platform_capability_service.dart Introduces platform capability gating (notably disables RAG on Windows).
examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart Adds proxy-aware HTTP client creation for example app network calls.
examples/flutter/RunAnywhereAI/lib/core/models/proxy_settings.dart Adds proxy settings model types (scope/scheme/settings).
examples/flutter/RunAnywhereAI/lib/core/design_system/unsupported_feature_view.dart Adds reusable “feature unavailable” UI component.
examples/flutter/RunAnywhereAI/lib/core/design_system/app_colors.dart Adds withValues compatibility helper and adjusts semantic color mapping.
examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart Configures proxy-aware HTTP client factory for model downloads and improves backend registration messaging.
examples/flutter/RunAnywhereAI/ios/Runner/GeneratedPluginRegistrant.m Updates plugin registrant to match updated plugin names (record_darwin, sqflite).
examples/flutter/RunAnywhereAI/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java Updates error log string for sqflite plugin naming.
examples/flutter/RunAnywhereAI/.gitignore Stops ignoring the windows/ folder so Windows runner files are tracked.
docs/building.md Documents Windows ONNX voice validation and Flutter Windows vision setup notes.
.gitignore Ignores .worktrees/ directory.

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

Comment on lines +14 to +33
rac_result_t rac_extract_archive_native(const char* archive_path, const char* destination_dir,
const rac_extraction_options_t* options,
rac_extraction_progress_fn progress_callback,
void* user_data, rac_extraction_result_t* out_result) {
(void)archive_path;
(void)destination_dir;
(void)options;
(void)progress_callback;
(void)user_data;
if (out_result) {
*out_result = {0, 0, 0, 0};
}
RAC_LOG_WARNING(kLogTag,
"Native archive extraction is disabled in this build");
return RAC_ERROR_NOT_SUPPORTED;
}

rac_bool_t rac_detect_archive_type(const char* file_path, rac_archive_type_t* out_type) {
(void)file_path;
if (out_type) {
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

This stub defines rac_extract_archive_native / rac_detect_archive_type, which are also defined in src/infrastructure/extraction/rac_extraction.cpp. If both files are ever compiled into the same target (e.g., when wiring this stub into the Windows build), it will cause duplicate symbol/linker errors. Ensure the build system conditionally compiles either the real libarchive implementation or this stub (e.g., switch sources based on RAC_PLATFORM_WINDOWS / a RAC_ENABLE_NATIVE_EXTRACTION option).

Copilot uses AI. Check for mistakes.
Comment on lines +48 to +51
bypassLocal == null &&
username.isEmpty &&
password.isEmpty) {
settings = await _loadLegacyGeneralSettings(prefs);
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

Legacy proxy migration in load() is gated on username.isEmpty && password.isEmpty. If a user has legacy prefs set (proxyEnabled/proxyHost/...) but also has stored credentials, this condition prevents _loadLegacyGeneralSettings from running, and the app will treat the proxy as disabled with empty host/port. Consider triggering legacy migration based solely on the absence of the new per-scope preference keys (enabled/scheme/host/port/bypassLocal), regardless of whether credentials exist.

Suggested change
bypassLocal == null &&
username.isEmpty &&
password.isEmpty) {
settings = await _loadLegacyGeneralSettings(prefs);
bypassLocal == null) {
final legacySettings = await _loadLegacyGeneralSettings(prefs);
settings = ProxySettings(
enabled: legacySettings.enabled,
scheme: legacySettings.scheme,
host: legacySettings.host,
port: legacySettings.port,
username: username,
password: password,
bypassLocal: legacySettings.bypassLocal,
);

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +27
for %%I in ("%~dp0.") do set "SCRIPT_HOME=%%~fI"
set "ROOT=%SCRIPT_HOME%"
set "WINDOWS_SCRIPT_DIR=%ROOT%\scripts\windows"
set "BUILD_DIR=%ROOT%\build-windows-x64"
set "DIST_DIR=%ROOT%\dist\windows\x64"
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

ROOT is set to the script directory (.../scripts) rather than the runanywhere-commons root. That makes WINDOWS_SCRIPT_DIR resolve to scripts\scripts\windows, and cmake -S "%ROOT%" will point at the wrong CMakeLists.txt (configure will fail). Compute the repo root as the parent of SCRIPT_HOME (or use %~dp0..) and build paths from there.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 19

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
examples/flutter/RunAnywhereAI/lib/features/voice/text_to_speech_view.dart (2)

80-83: ⚠️ Potential issue | 🟡 Minor

Skip audio-player initialization when TTS is unsupported.

Line 274 gates the UI, but Line 82 still initializes audio playback on unsupported platforms. Keep the side effects behind the same capability check so unsupported platforms only render the fallback view.

🛡️ Proposed fix
   `@override`
   void initState() {
     super.initState();
-    unawaited(_initializeAudioPlayer());
+    if (PlatformCapabilityService.shared.supportsTextToSpeech) {
+      unawaited(_initializeAudioPlayer());
+    }
   }

Also applies to: 274-285

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/lib/features/voice/text_to_speech_view.dart`
around lines 80 - 83, The initState currently always calls
unawaited(_initializeAudioPlayer()), causing audio initialization on platforms
that don't support TTS; change initState so it only calls
_initializeAudioPlayer() when the same capability check used for the UI fallback
is true (the same predicate/method used around the UI branch at the TTS
rendering code). Update initState to check the TTS-support predicate (the same
boolean or method used in the TTS UI branch) before calling
unawaited(_initializeAudioPlayer()), ensuring platforms that render the fallback
view do not run audio-player initialization.

123-181: ⚠️ Potential issue | 🔴 Critical

System TTS path does not load via SDK, causing _generateSpeech() to fail immediately on the isTTSVoiceLoaded check.

In ModelSelectionSheet._onSystemTTSRow() (lines 458–481), selecting system TTS invokes onModelSelected(systemTTSModel) directly without calling any load function. The callback _refreshModelState() detects the system TTS preference and returns early (line 139) after updating local UI state only.

Later, _generateSpeech() (line 178) unconditionally checks if (!sdk.RunAnywhere.isTTSVoiceLoaded) with no bypass for system TTS. Since system TTS selection never called loadTTSVoice() or equivalent, isTTSVoiceLoaded remains false, causing the error "TTS component not loaded" even though a voice is visibly selected.

In contrast, non-system TTS selection (line 524) goes through _viewModel.selectModel(model), which calls loadTTSVoice(model.id) first, so isTTSVoiceLoaded is true by the time _generateSpeech() runs.

Either system TTS must also call a load function before returning from _refreshModelState(), or _generateSpeech() must handle system TTS as a separate path that does not require isTTSVoiceLoaded.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/lib/features/voice/text_to_speech_view.dart`
around lines 123 - 181, Selecting a system TTS model only updates UI state in
_refreshModelState (early return when model.preferredFramework ==
LLMFramework.systemTTS) but never loads the TTS component, so _generateSpeech
fails on sdk.RunAnywhere.isTTSVoiceLoaded; fix by loading the system TTS when
system is selected (mirror non-system flow) — e.g., in the branch inside
_refreshModelState (or in ModelSelectionSheet._onSystemTTSRow) call the same
loading path used by _viewModel.selectModel (or invoke the SDK system-TTS load
function) so sdk.RunAnywhere.isTTSVoiceLoaded becomes true, or alternatively
update _generateSpeech to bypass the isTTSVoiceLoaded check for system TTS by
detecting _isSystemTTS; target symbols: _refreshModelState,
ModelSelectionSheet._onSystemTTSRow, _viewModel.selectModel,
sdk.RunAnywhere.isTTSVoiceLoaded, loadTTSVoice/_viewModel.loadTTSVoice.
examples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dart (1)

88-91: ⚠️ Potential issue | 🟡 Minor

Guard the init-time permission check with the same capability gate.

Line 267 prevents rendering STT on unsupported platforms, but Line 90 still calls the STT permission service before build() can return the unsupported view. On platforms where STT permissions/plugins are unavailable, this can fail even though the feature is gated.

🛡️ Proposed fix
   `@override`
   void initState() {
     super.initState();
-    unawaited(_checkMicrophonePermission());
+    if (PlatformCapabilityService.shared.supportsSpeechToText) {
+      unawaited(_checkMicrophonePermission());
+    }
   }

Also applies to: 267-278

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dart`
around lines 88 - 91, The initState currently calls _checkMicrophonePermission()
unconditionally; guard that call with the same capability check used in build
(the STT/platform support gate) so we only request permissions when
speech-to-text is available—e.g., in initState, wrap
unawaited(_checkMicrophonePermission()) inside the same boolean check used to
decide whether to render STT in build; apply the same guarding to the other
init-time permission checks referenced around the build/STT gate (lines
~267-278) so no STT plugin calls run on unsupported platforms.
sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt (2)

284-297: ⚠️ Potential issue | 🟡 Minor

Duplicate elseif(RAC_PLATFORM_WINDOWS) — second branch is unreachable.

Same issue as the upper chain: the branch at lines 294-297 is dead (first match at 284-288 wins). The dead branch is only a message() here so behavior is not affected, but please remove it to avoid confusion / future drift.

🩹 Proposed fix
 elseif(RAC_PLATFORM_LINUX)
     message(STATUS "Configuring LlamaCPP backend for Linux")
     # Linux-specific link libraries
     target_link_libraries(rac_backend_llamacpp PUBLIC pthread dl)
-
-elseif(RAC_PLATFORM_WINDOWS)
-    message(STATUS "Configuring LlamaCPP backend for Windows")
-    # No extra link libraries needed on Windows (threading is built-in)
 endif()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt` around lines
284 - 297, Remove the duplicate elseif(RAC_PLATFORM_WINDOWS) branch that appears
after the Linux branch; keep the first Windows branch that configures
rac_backend_llamacpp (the message(),
target_compile_definitions(rac_backend_llamacpp PRIVATE NOMINMAX
WIN32_LEAN_AND_MEAN) and target_link_libraries(rac_backend_llamacpp PUBLIC
ws2_32)) and delete the second redundant elseif(RAC_PLATFORM_WINDOWS) block (the
extra message() and comment) so only one Windows conditional for
RAC_PLATFORM_WINDOWS remains.

76-113: ⚠️ Potential issue | 🔴 Critical

Critical: duplicate elseif(RAC_PLATFORM_WINDOWS) — second branch is dead code and drops intended settings.

Two Windows branches exist in the same if/elseif chain. CMake takes the first match (lines 76-85) and silently skips lines 100-113, so:

  • GGML_OPENMP OFF (line 110) is never applied on Windows, contradicting the second block's clear intent (and the message "...Windows (CPU-only)"). On MSVC this can produce OpenMP link/runtime surprises vs. the other disabled-OpenMP platforms (Android).
  • The status message on line 112 is never printed.

The AI summary claimed one duplicate was removed, but both are still present — please collapse them into a single branch that preserves GGML_OPENMP OFF and the status message.

🩹 Proposed fix
 elseif(RAC_PLATFORM_WINDOWS)
     set(GGML_METAL OFF CACHE BOOL "" FORCE)
     set(GGML_VULKAN OFF CACHE BOOL "" FORCE)
     set(GGML_CUDA OFF CACHE BOOL "" FORCE)
     set(GGML_OPENCL OFF CACHE BOOL "" FORCE)
     set(GGML_HIPBLAS OFF CACHE BOOL "" FORCE)
     set(GGML_SYCL OFF CACHE BOOL "" FORCE)
     set(GGML_KOMPUTE OFF CACHE BOOL "" FORCE)
     set(GGML_RPC OFF CACHE BOOL "" FORCE)
+    set(GGML_OPENMP OFF CACHE BOOL "" FORCE)
     set(GGML_NATIVE OFF CACHE BOOL "" FORCE)
+    message(STATUS "Configuring llama.cpp for Windows (CPU-only)")
 elseif(RAC_PLATFORM_LINUX)
     ...
-elseif(RAC_PLATFORM_WINDOWS)
-    # Windows: CPU-only by default, no Metal/CUDA/Vulkan
-    set(GGML_METAL OFF CACHE BOOL "" FORCE)
-    set(GGML_CUDA OFF CACHE BOOL "" FORCE)
-    set(GGML_VULKAN OFF CACHE BOOL "" FORCE)
-    set(GGML_OPENCL OFF CACHE BOOL "" FORCE)
-    set(GGML_HIPBLAS OFF CACHE BOOL "" FORCE)
-    set(GGML_SYCL OFF CACHE BOOL "" FORCE)
-    set(GGML_KOMPUTE OFF CACHE BOOL "" FORCE)
-    set(GGML_RPC OFF CACHE BOOL "" FORCE)
-    set(GGML_OPENMP OFF CACHE BOOL "" FORCE)
-    set(GGML_NATIVE OFF CACHE BOOL "" FORCE)
-    message(STATUS "Configuring llama.cpp for Windows (CPU-only)")
 endif()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt` around lines 76
- 113, There are two elseif(RAC_PLATFORM_WINDOWS) branches causing the first to
shadow the second and never apply the Windows-specific settings; collapse them
into a single elseif(RAC_PLATFORM_WINDOWS) branch that combines all intended
settings from both blocks (e.g., keep set(GGML_METAL OFF ...), set(GGML_CUDA OFF
...), set(GGML_VULKAN OFF ...), set(GGML_OPENCL OFF ...), set(GGML_HIPBLAS OFF
...), set(GGML_SYCL OFF ...), set(GGML_KOMPUTE OFF ...), set(GGML_RPC OFF ...),
set(GGML_NATIVE OFF ...), and ensure set(GGML_OPENMP OFF CACHE BOOL "" FORCE) is
included) and preserve the message(STATUS "Configuring llama.cpp for Windows
(CPU-only)"); remove the duplicate elseif to avoid dead code.
sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart (1)

226-232: ⚠️ Potential issue | 🟡 Minor

Stale // ignore: unused_element annotation.

_authenticateWithBackend is still invoked at Line 162 of this file, so // ignore: unused_element is misleading and will hide a real signal if the method ever actually becomes unused. Please drop the directive.

Proposed fix
   /// Authenticate with backend for production/staging environments.
   /// Matches Swift: CppBridge.Auth.authenticate(apiKey:) in setupHTTP()
-  // ignore: unused_element
   static Future<void> _authenticateWithBackend(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart`
around lines 226 - 232, Remove the stale analyzer ignore before the private
method: delete the "// ignore: unused_element" annotation immediately above the
static Future<void> _authenticateWithBackend(SDKInitParams params, SDKLogger
logger) declaration so the analyzer will correctly report if the method becomes
unused; ensure no other suppressions are added and run the analyzer to confirm
there are no unintended warnings after the change (the method is referenced
elsewhere in this file).
🟡 Minor comments (17)
examples/flutter/RunAnywhereAI/lib/features/voice/voice_assistant_view.dart-321-330 (1)

321-330: ⚠️ Potential issue | 🟡 Minor

Dead branch: supportsVoiceAssistant is always true.

Same concern as vision_hub_view.dart: this guard is currently unreachable because PlatformCapabilityService.supportsVoiceAssistant is hardcoded to true. If the intent (per PR messaging) is that voice features aren't enabled in the Windows vertical-slice build, either wire the capability flag to !Platform.isWindows, or drop the guard until there's a real condition to gate on. Otherwise users on Windows will hit runtime failures further down the pipeline (STT/LLM/TTS availability) rather than this clean fallback.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/lib/features/voice/voice_assistant_view.dart`
around lines 321 - 330, The guard using
PlatformCapabilityService.shared.supportsVoiceAssistant is dead because
supportsVoiceAssistant is hardcoded true; either make the flag reflect the
Windows exclusion by changing PlatformCapabilityService.supportsVoiceAssistant
to return !Platform.isWindows (or otherwise check Platform.isWindows where the
flag is computed) so the UnsupportedFeatureView path can execute on Windows, or
remove this unreachable guard in voice_assistant_view.dart and rely on
downstream runtime checks; update any callsites using
PlatformCapabilityService.supportsVoiceAssistant and keep
PlatformCapabilityService.shared.unsupportedMessage('Voice Assistant') for the
fallback messaging if you choose the flag route.
examples/flutter/RunAnywhereAI/lib/features/vision/vision_hub_view.dart-19-30 (1)

19-30: ⚠️ Potential issue | 🟡 Minor

Dead branch under current capability config.

PlatformCapabilityService.supportsVision is hardcoded to true, so this guard never triggers. If Vision is intended to be unsupported on Windows (consistent with the rest of the PR messaging), supportsVision should be !Platform.isWindows (similar to supportsRag). Otherwise, consider dropping this block until the capability becomes dynamic.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/lib/features/vision/vision_hub_view.dart`
around lines 19 - 30, The guard for vision being unsupported is dead because
PlatformCapabilityService.supportsVision is hardcoded true; update the
capability check in PlatformCapabilityService (the supportsVision property) to
mirror the intended platform rule (e.g., return !Platform.isWindows as done for
supportsRag) so the vision branch can trigger on Windows, or remove the unused
guard in vision_hub_view.dart if you intend to keep supportsVision always true;
locate PlatformCapabilityService.supportsVision (and related uses like
capability.unsupportedMessage) to make the change.
examples/flutter/RunAnywhereAI/lib/core/design_system/app_colors.dart-38-38 (1)

38-38: ⚠️ Potential issue | 🟡 Minor

Replace deprecated surfaceVariant with surfaceContainerHighest.

ColorScheme.surfaceVariant was deprecated in Material 3 (v3.18.0+) in favor of surfaceContainerHighest. The current code will produce deprecation warnings and break once the symbol is removed. Replace with Theme.of(context).colorScheme.surfaceContainerHighest.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/lib/core/design_system/app_colors.dart` at
line 38, Replace the deprecated ColorScheme.surfaceVariant usage with
ColorScheme.surfaceContainerHighest: update the code that currently reads
Theme.of(context).colorScheme.surfaceVariant to
Theme.of(context).colorScheme.surfaceContainerHighest (in the app_colors logic
where Theme.of(context).colorScheme is accessed) to remove deprecation warnings
and ensure compatibility with Material 3 v3.18.0+.
examples/flutter/RunAnywhereAI/lib/core/services/platform_capability_service.dart-1-20 (1)

1-20: ⚠️ Potential issue | 🟡 Minor

dart:io breaks Flutter Web; message text is Windows-specific but reused for any unsupported feature.

Two concerns:

  1. import 'dart:io' and Platform.isWindows are unavailable on Flutter Web. If this app is (or may be) built for web, consumers should use kIsWeb from foundation.dart before touching Platform. If web is not a target, this is fine — please confirm.
  2. unsupportedMessage hardcodes "…not enabled in the Windows vertical-slice build yet." That text is reused verbatim by vision_hub_view, voice_assistant_view, speech_to_text_view, text_to_speech_view, and rag_demo_view. Today only supportsRag can be false (and only on Windows), so it's accurate in practice — but this will become misleading the moment another flag is flipped to false on a non-Windows platform. Consider deriving the platform name dynamically, or taking the platform/reason as a parameter.
♻️ Suggested refactor
-  String unsupportedMessage(String featureName) =>
-      '$featureName is not enabled in the Windows vertical-slice build yet.';
+  String unsupportedMessage(String featureName) {
+    final platform = Platform.isWindows
+        ? 'Windows'
+        : Platform.operatingSystem;
+    return '$featureName is not enabled in this $platform build yet.';
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@examples/flutter/RunAnywhereAI/lib/core/services/platform_capability_service.dart`
around lines 1 - 20, PlatformCapabilityService currently imports dart:io and
uses Platform.isWindows (breaking Flutter Web) and returns a hardcoded
Windows-specific message in unsupportedMessage; fix by guarding platform checks
with kIsWeb from package:flutter/foundation.dart before referencing Platform (or
document if web is not a target) and make unsupportedMessage accept an optional
platform/reason parameter or derive the platform name dynamically (e.g., pass in
a string reason when calling unsupportedMessage) so the message isn’t always
Windows-specific; update references to PlatformCapabilityService.supportsRag and
unsupportedMessage accordingly.
examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_vlm_service.dart-28-40 (1)

28-40: ⚠️ Potential issue | 🟡 Minor

Stream subscription cancellation does not stop the underlying SDK generation.

The VLMStreamingResult returned by the SDK includes an explicit cancel() function that calls DartBridge.vlm.cancel() to halt C++ processing. However, processImageStream() only exposes result.stream to callers and does not retain or expose the cancel() function. When a consumer cancels the stream subscription, it closes the stream but does NOT invoke the SDK's cancel function, leaving the underlying generation running until completion. Callers must explicitly invoke cancelGeneration() to stop work—this is not obvious from the stream API.

Consider either:

  1. Exposing the full VLMStreamingResult (with explicit cancel() access) instead of just the stream
  2. Wiring stream subscription cancellation to call result.cancel() internally
  3. Adding clear documentation that stream cancellation does not stop generation; cancelGeneration() must be called explicitly
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_vlm_service.dart`
around lines 28 - 40, processImageStream currently yields result.stream but
doesn't call the SDK's cancel() when the consumer cancels; update
processImageStream to create a StreamController, call
sdk.RunAnywhere.processImageStream(...) to get the VLMStreamingResult, add
result.stream to the controller (or forward events), and implement
onCancel/onPause to invoke result.cancel() so underlying C++ generation is
stopped when the Dart subscription is cancelled (keep the public API name
processImageStream and ensure any errors from result.stream are forwarded and
the controller is closed).
examples/flutter/RunAnywhereAI/lib/features/vision/vlm_view_model.dart-62-68 (1)

62-68: ⚠️ Potential issue | 🟡 Minor

_hasCameraDevice = false path leaks a prior session.

If initializeCamera() is called a second time and listDevices() now returns empty (e.g., user unplugged a USB camera, or camera permission was revoked between calls), we set _isCameraInitialized = false and return, but any previously stored _cameraSession is not disposed or cleared. Consider disposing it here (same pattern used on line 76) to avoid leaking the underlying CameraController.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/lib/features/vision/vlm_view_model.dart`
around lines 62 - 68, The early-return branch in initializeCamera() sets
_hasCameraDevice = false and _isCameraInitialized = false but does not dispose
or clear any existing _cameraSession, leaking the previous CameraController;
update this branch to mirror the cleanup used on the other path (see the
disposal pattern around line 76) by disposing the existing
_cameraSession/controller if non-null and then setting _cameraSession (and any
related state) to null/cleared before calling _safeNotifyListeners() and
returning.
examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dart-33-44 (1)

33-44: ⚠️ Potential issue | 🟡 Minor

device.handle! will crash for any device not produced by this backend.

createSession force-unwraps device.handle and casts to CameraDescription. If a caller ever constructs a VisionCameraDevice directly (e.g., a different backend, a deserialized device, or a test double driving the real implementation), this throws TypeError/Null check operator used on a null value. Since the abstraction is explicitly designed to be backend-agnostic, the contract should either (a) require handle non-null and document it, or (b) fail with a clear error.

🛡️ Suggested defensive check
   VisionCameraSession createSession(VisionCameraDevice device) {
-    final description = device.handle! as CameraDescription;
+    final handle = device.handle;
+    if (handle is! CameraDescription) {
+      throw ArgumentError(
+        'CameraPluginVisionCameraBackend requires a VisionCameraDevice '
+        'produced by listDevices(); received handle of type '
+        '${handle.runtimeType}.',
+      );
+    }
     return _CameraPluginVisionCameraSession(
       CameraController(
-        description,
+        handle,
         ResolutionPreset.medium,
         imageFormatGroup: ImageFormatGroup.bgra8888,
       ),
     );
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dart`
around lines 33 - 44, createSession currently force-unwraps
VisionCameraDevice.handle and casts to CameraDescription which will crash for
devices not produced by this backend; update
CameraPluginVisionCameraBackend.createSession to validate that device.handle is
non-null and is a CameraDescription before casting (e.g., check device.handle !=
null && device.handle is CameraDescription) and if the check fails throw a clear
ArgumentError/StateError with a message referencing VisionCameraDevice.handle
and CameraDescription so callers get a descriptive failure; then use the
validated CameraDescription to construct the CameraController and
_CameraPluginVisionCameraSession.
sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.cpp-51-54 (1)

51-54: ⚠️ Potential issue | 🟡 Minor

Hardcoded backend version/name will drift from the actual library.

getBackendVersion returns hardcoded "0.1.5", while iOS and Android hardcode "0.1.4" — the version mismatch demonstrates the drift problem. Neither implementation queries the rac_backend_llamacpp backend that the CMake POST_BUILD copies next to the plugin. When the bundled llamacpp backend is bumped, the reported version becomes stale.

Consider exposing a C symbol from rac_backend_llamacpp (e.g. rac_backend_llamacpp_version()) and calling it here, or derive the constant from the same source-of-truth the build uses (e.g. a generated header) so all platforms stay in sync.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.cpp`
around lines 51 - 54, getBackendVersion/getBackendName currently return
hardcoded strings that will drift; instead, declare and call an exported C
function from the native backend (e.g. rac_backend_llamacpp_version()) or read a
generated shared header constant and use its value in the plugin. Concretely:
add an extern "C" declaration for rac_backend_llamacpp_version() in
runanywhere_llamacpp_plugin.cpp, call it inside the getBackendVersion branch to
return its string, and similarly derive getBackendName (or call an exported
rac_backend_llamacpp_name() if available); ensure you link against
rac_backend_llamacpp so the symbol resolves at runtime and remove the hardcoded
literals.
sdk/runanywhere-commons/README.md-267-280 (1)

267-280: ⚠️ Potential issue | 🟡 Minor

Fix the Windows ONNX artifact list.

The ONNX build instructions list rac_backend_llamacpp.dll but omit the expected ONNX backend output, which can mislead users validating build-windows.bat onnx.

📝 Proposed doc fix
 Artifacts:
 - `dist/windows/x64/rac_commons.dll`
-- `dist/windows/x64/rac_backend_llamacpp.dll`
+- `dist/windows/x64/rac_backend_onnx.dll`
+- `dist/windows/x64/rac_backend_llamacpp.dll` when building `llamacpp` or `all`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-commons/README.md` around lines 267 - 280, Update the Windows
ONNX Dependencies section in README.md to include the ONNX backend artifact
produced by running "cmd /c scripts\build-windows.bat onnx": add the missing
artifact name (e.g., rac_backend_onnx.dll) to the Artifacts list alongside
rac_commons.dll and rac_backend_llamacpp.dll and note that it is produced by the
"onnx" build target to avoid confusion when validating build-windows.bat onnx.
examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart-365-378 (1)

365-378: ⚠️ Potential issue | 🟡 Minor

Avoid Windows-specific failure text in shared app code.

These catch blocks run in the common Flutter app. On non-Windows platforms, “this Windows build” makes real ONNX/RAG registration failures look platform-specific when they are not.

Proposed wording cleanup
     try {
       await Onnx.register();
-      debugPrint('✅ ONNX backend registered (Windows optional)');
+      debugPrint('✅ ONNX backend registered');
     } catch (e) {
-      debugPrint('⚠️ ONNX backend not available on this Windows build: $e');
+      debugPrint('⚠️ ONNX backend not available on this build: $e');
     }
@@
     try {
       await RAGModule.register();
       debugPrint('✅ RAG backend registered');
     } catch (e) {
-      debugPrint('⚠️ RAG backend not available on this Windows build: $e');
+      debugPrint('⚠️ RAG backend not available on this build: $e');
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart` around lines
365 - 378, The catch messages for Onnx.register and RAGModule.register
incorrectly state "this Windows build" which is misleading on non-Windows
platforms; update the catch handlers for Onnx.register and RAGModule.register to
use neutral, platform-agnostic failure messages (e.g., "ONNX backend not
available" and "RAG backend not available") and include the caught exception
details in the debug output so errors remain informative across all platforms.
sdk/runanywhere-commons/src/infrastructure/download/download_orchestrator.cpp-150-162 (1)

150-162: ⚠️ Potential issue | 🟡 Minor

Consider tightening the mmproj match to the llama.cpp convention.

find("mmproj") != npos matches anywhere in the filename, so a legitimate model whose name happens to contain the substring (e.g. an unusually named weight file, or a future sibling artifact) would be silently skipped. The llama.cpp / gguf_split convention is an mmproj-*.gguf prefix (or bare mmproj.gguf). A prefix check after stripping directory components is both more precise and easier to reason about.

♻️ Suggested tightening
 static bool is_auxiliary_model_file(const std::string& filename) {
     std::string lower(filename);
     for (auto& c : lower) c = static_cast<char>(tolower(c));
-    return lower.find("mmproj") != std::string::npos;
+    // llama.cpp multimodal packages ship the projector as `mmproj-*.gguf`
+    // (occasionally bare `mmproj.gguf`). Match the prefix, not any substring.
+    return lower.rfind("mmproj-", 0) == 0 || lower == "mmproj.gguf";
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-commons/src/infrastructure/download/download_orchestrator.cpp`
around lines 150 - 162, The is_auxiliary_model_file function is too permissive
by matching "mmproj" anywhere; change it to operate on the filename's basename
(strip directory components) and perform a case-insensitive check that only
returns true for the llama.cpp conventions: either the exact basename
"mmproj.gguf" or a basename that starts with "mmproj-" (e.g., "mmproj-*.gguf");
update is_auxiliary_model_file to extract the basename, normalize case, and then
test for equality to "mmproj.gguf" or a prefix "mmproj-" rather than using find
anywhere in the string.
examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart-234-406 (1)

234-406: ⚠️ Potential issue | 🟡 Minor

Minor: TextEditingControllers in the proxy dialog are never disposed.

hostController, portController, usernameController, and passwordController (lines 236-242) are allocated each time the dialog opens but are never .dispose()-ed — every Configure → close cycle leaks four controllers (and their internal listeners). Dispose them when the dialog future completes:

     await showDialog<void>(
       context: context,
       builder: (dialogContext) => StatefulBuilder(
         ...
       ),
     );
+    hostController.dispose();
+    portController.dispose();
+    usernameController.dispose();
+    passwordController.dispose();
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart`
around lines 234 - 406, The TextEditingController instances created in
_showProxyConfigDialog (hostController, portController, usernameController,
passwordController) are never disposed, causing leaks; wrap the showDialog call
in a try/finally (or ensure after the await showDialog completes) and call
dispose() on each controller in the finally block so they are always cleaned up
(dispose hostController, portController, usernameController,
passwordController), leaving the existing dialog logic unchanged.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/flutter_window.cpp-45-66 (1)

45-66: ⚠️ Potential issue | 🟡 Minor

Potential null-deref on WM_FONTCHANGE when flutter_controller_ is null.

The guard on Line 50 short-circuits only the HandleTopLevelWindowProc call, but the switch on Lines 59-63 unconditionally dereferences flutter_controller_->engine(). If a WM_FONTCHANGE arrives before OnCreate completes or after OnDestroy nulls the controller, this crashes.

That said, this file is vendored from flutter_secure_storage_windows's example and mirrors the upstream Flutter Windows runner template — the same latent issue exists upstream. If the intent is strictly to vendor unchanged, leave it alone; otherwise a two-line guard is trivial:

Proposed guard (only if patching the vendored copy is acceptable)
   switch (message) {
     case WM_FONTCHANGE:
-      flutter_controller_->engine()->ReloadSystemFonts();
+      if (flutter_controller_ && flutter_controller_->engine()) {
+        flutter_controller_->engine()->ReloadSystemFonts();
+      }
       break;
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/flutter_window.cpp`
around lines 45 - 66, The WM_FONTCHANGE handling can dereference
flutter_controller_ when it is null; update FlutterWindow::MessageHandler to
check flutter_controller_ before calling
flutter_controller_->engine()->ReloadSystemFonts() (i.e., only call
engine()->ReloadSystemFonts() if flutter_controller_ is non-null), so the switch
case for WM_FONTCHANGE is guarded against controller lifetime between
OnCreate/OnDestroy; locate the WM_FONTCHANGE case in
FlutterWindow::MessageHandler and add the null-check around flutter_controller_
before invoking engine().
sdk/runanywhere-flutter/packages/runanywhere/windows/CMakeLists.txt-35-55 (1)

35-55: ⚠️ Potential issue | 🟡 Minor

DLL discovery is tightly coupled to the monorepo layout.

Both candidate paths assume the plugin is being built from inside runanywhere-sdks (the first resolves 4 levels up from the example app; the second 3 levels up from the package). An external Flutter app depending on this plugin via pub won't have sdk/runanywhere-commons/dist/windows/x64/ anywhere near those relative locations, and FATAL_ERROR will block CMake configuration outright.

Consider:

  1. Supporting an override env var / CMake cache var (e.g., RAC_COMMONS_DLL_PATH or ENV{RAC_COMMONS_DLL}) that takes precedence over the candidate list.
  2. When the plugin is consumed externally, bundling rac_commons.dll under the plugin's own windows/ directory (e.g., ${CMAKE_CURRENT_SOURCE_DIR}/native/rac_commons.dll) as the primary candidate, falling back to the monorepo layout only for in-tree builds.

This will also make the error message in Lines 52-54 more actionable for external users, since pointing them at scripts/build-windows.bat only helps monorepo contributors.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-flutter/packages/runanywhere/windows/CMakeLists.txt` around
lines 35 - 55, The DLL discovery is too tied to monorepo layout; modify the
logic around COMMONS_DLL_CANDIDATES/COMMONS_DLL to first honor an override cache
variable or env var (e.g., check a CMake cache var RAC_COMMONS_DLL_PATH or
ENV{RAC_COMMONS_DLL} and use it if set), then prefer a plugin-local candidate
such as "${CMAKE_CURRENT_SOURCE_DIR}/native/rac_commons.dll" before falling back
to the existing repository-relative candidates, and update the fatal message to
mention the override option and the plugin-local location instead of only the
monorepo build script.
docs/building.md-165-170 (1)

165-170: ⚠️ Potential issue | 🟡 Minor

Include rac_commons.dll in the Windows runtime checklist.

The Windows build script treats rac_commons.dll as required and stages it into dist, but this validation list omits it.

Proposed docs update
 - The final Windows runner directory should contain:
+  - `rac_commons.dll`
   - `rac_backend_onnx.dll`
   - `onnxruntime.dll`
   - `onnxruntime_providers_shared.dll`
   - `sherpa-onnx-c-api.dll`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/building.md` around lines 165 - 170, The Windows runtime checklist in
docs (the list containing `rac_backend_onnx.dll`, `onnxruntime.dll`,
`onnxruntime_providers_shared.dll`, `sherpa-onnx-c-api.dll`, and companion
runtime DLLs) is missing `rac_commons.dll`; update that list to include
`rac_commons.dll` so it matches the Windows build script which stages
`rac_commons.dll` into `dist`.
sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/CMakeLists.txt-55-59 (1)

55-59: ⚠️ Potential issue | 🟡 Minor

Add VERBATIM to the post-build copy command.

On Windows, paths with spaces in the checkout directory (e.g., C:\Users\...\My Project) can be mishandled by CMake without VERBATIM. The keyword ensures proper escaping for the platform's build tool, preventing argument splitting or mangled quotes that would cause the DLL copy to fail.

Proposed fix
 add_custom_command(TARGET ${PLUGIN_NAME} POST_BUILD
   COMMAND ${CMAKE_COMMAND} -E copy_if_different
   "${LLAMA_BACKEND_DLL}"
   "$<TARGET_FILE_DIR:${PLUGIN_NAME}>/rac_backend_llamacpp.dll"
+  VERBATIM
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/CMakeLists.txt`
around lines 55 - 59, The post-build add_custom_command for target
${PLUGIN_NAME} may mis-handle paths with spaces; update the add_custom_command
invocation that copies "${LLAMA_BACKEND_DLL}" to
"$<TARGET_FILE_DIR:${PLUGIN_NAME}>/rac_backend_llamacpp.dll" to include the
VERBATIM keyword so CMake will properly escape arguments on Windows (i.e., add
VERBATIM to the command invocation for the add_custom_command targeting
${PLUGIN_NAME}).
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/flutter_secure_storage_windows_plugin.cpp-184-186 (1)

184-186: ⚠️ Potential issue | 🟡 Minor

Incorrect STATUS_SUCCESS mapping.

STATUS_SUCCESS is 0x00000000, not 0xC0000000. The current case is unreachable (BCRYPT_SUCCESS checks already short-circuit success) and the constant itself is wrong, which will mislead anyone diagnosing errors via the returned string. Either drop this case or fix the constant.

🔧 Proposed fix
-      case 0xc0000000:
-          oss << " (STATUS_SUCCESS)";
-          break;
+      case 0x00000000:
+          oss << " (STATUS_SUCCESS)";
+          break;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/flutter_secure_storage_windows_plugin.cpp`
around lines 184 - 186, The switch contains an incorrect and unreachable mapping
for STATUS_SUCCESS using the value 0xC0000000; update the switch handling (the
case labeled "case 0xc0000000") to either be removed or corrected to
STATUS_SUCCESS's real value 0x00000000 and adjust the appended string to
"(STATUS_SUCCESS)"; note that BCRYPT_SUCCESS paths already short-circuit
success, so removing the bogus case is also acceptable—make the change in the
same switch block that formats NTSTATUS/BCRYPT return codes in
flutter_secure_storage_windows_plugin.cpp.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ca23ca24-4448-4d91-b5ee-51e1ea758286

📥 Commits

Reviewing files that changed from the base of the PR and between 094d569 and 7df8df2.

⛔ Files ignored due to path filters (2)
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/resources/app_icon.ico is excluded by !**/*.ico
  • examples/flutter/RunAnywhereAI/windows/runner/resources/app_icon.ico is excluded by !**/*.ico
📒 Files selected for processing (123)
  • .gitignore
  • docs/building.md
  • examples/flutter/RunAnywhereAI/.gitignore
  • examples/flutter/RunAnywhereAI/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java
  • examples/flutter/RunAnywhereAI/ios/Runner/GeneratedPluginRegistrant.m
  • examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart
  • examples/flutter/RunAnywhereAI/lib/core/design_system/app_colors.dart
  • examples/flutter/RunAnywhereAI/lib/core/design_system/unsupported_feature_view.dart
  • examples/flutter/RunAnywhereAI/lib/core/models/proxy_settings.dart
  • examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart
  • examples/flutter/RunAnywhereAI/lib/core/services/platform_capability_service.dart
  • examples/flutter/RunAnywhereAI/lib/core/services/proxy_settings_service.dart
  • examples/flutter/RunAnywhereAI/lib/core/utilities/constants.dart
  • examples/flutter/RunAnywhereAI/lib/features/chat/chat_interface_view.dart
  • examples/flutter/RunAnywhereAI/lib/features/models/model_list_view_model.dart
  • examples/flutter/RunAnywhereAI/lib/features/models/model_selection_sheet.dart
  • examples/flutter/RunAnywhereAI/lib/features/rag/rag_demo_view.dart
  • examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart
  • examples/flutter/RunAnywhereAI/lib/features/settings/tool_settings_view_model.dart
  • examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dart
  • examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_permission_gateway.dart
  • examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_vlm_service.dart
  • examples/flutter/RunAnywhereAI/lib/features/vision/vision_hub_view.dart
  • examples/flutter/RunAnywhereAI/lib/features/vision/vlm_camera_view.dart
  • examples/flutter/RunAnywhereAI/lib/features/vision/vlm_view_model.dart
  • examples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dart
  • examples/flutter/RunAnywhereAI/lib/features/voice/text_to_speech_view.dart
  • examples/flutter/RunAnywhereAI/lib/features/voice/voice_assistant_view.dart
  • examples/flutter/RunAnywhereAI/pubspec.yaml
  • examples/flutter/RunAnywhereAI/test/features/vision/vlm_view_model_test.dart
  • examples/flutter/RunAnywhereAI/test/widget_test.dart
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/CHANGELOG.md
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/LICENSE
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/README.md
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/analysis_options.yaml
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/README.md
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/analysis_options.yaml
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/integration_test/app_test.dart
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/lib/main.dart
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/pubspec.yaml
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/CMakeLists.txt
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/flutter/CMakeLists.txt
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/flutter/generated_plugin_registrant.cc
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/flutter/generated_plugin_registrant.h
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/flutter/generated_plugins.cmake
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/CMakeLists.txt
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/Runner.rc
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/flutter_window.cpp
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/flutter_window.h
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/main.cpp
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/resource.h
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/runner.exe.manifest
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/utils.cpp
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/utils.h
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/win32_window.cpp
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/win32_window.h
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/flutter_secure_storage_windows.dart
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_stub.dart
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/pubspec.yaml
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/test/unit_test.dart
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/CMakeLists.txt
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/flutter_secure_storage_windows_plugin.cpp
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/flutter_secure_storage_windows_plugin.h
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/include/flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h
  • examples/flutter/RunAnywhereAI/windows/.gitignore
  • examples/flutter/RunAnywhereAI/windows/CMakeLists.txt
  • examples/flutter/RunAnywhereAI/windows/flutter/CMakeLists.txt
  • examples/flutter/RunAnywhereAI/windows/flutter/generated_plugin_registrant.cc
  • examples/flutter/RunAnywhereAI/windows/flutter/generated_plugin_registrant.h
  • examples/flutter/RunAnywhereAI/windows/flutter/generated_plugins.cmake
  • examples/flutter/RunAnywhereAI/windows/runner/CMakeLists.txt
  • examples/flutter/RunAnywhereAI/windows/runner/Runner.rc
  • examples/flutter/RunAnywhereAI/windows/runner/flutter_window.cpp
  • examples/flutter/RunAnywhereAI/windows/runner/flutter_window.h
  • examples/flutter/RunAnywhereAI/windows/runner/main.cpp
  • examples/flutter/RunAnywhereAI/windows/runner/resource.h
  • examples/flutter/RunAnywhereAI/windows/runner/runner.exe.manifest
  • examples/flutter/RunAnywhereAI/windows/runner/utils.cpp
  • examples/flutter/RunAnywhereAI/windows/runner/utils.h
  • examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp
  • examples/flutter/RunAnywhereAI/windows/runner/win32_window.h
  • sdk/runanywhere-commons/CMakeLists.txt
  • sdk/runanywhere-commons/README.md
  • sdk/runanywhere-commons/scripts/build-windows.bat
  • sdk/runanywhere-commons/scripts/windows/download-sherpa-onnx.bat
  • sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt
  • sdk/runanywhere-commons/src/backends/onnx/CMakeLists.txt
  • sdk/runanywhere-commons/src/backends/onnx/onnx_backend.cpp
  • sdk/runanywhere-commons/src/backends/onnx/rac_backend_onnx_register.cpp
  • sdk/runanywhere-commons/src/core/rac_core.cpp
  • sdk/runanywhere-commons/src/infrastructure/download/download_orchestrator.cpp
  • sdk/runanywhere-commons/src/infrastructure/extraction/rac_extraction_stub.cpp
  • sdk/runanywhere-commons/tests/test_download_orchestrator.cpp
  • sdk/runanywhere-flutter/packages/runanywhere/README.md
  • sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/internal/vlm_file_resolution.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_model_registry.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_stt.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_tts.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/native/platform_loader.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart
  • sdk/runanywhere-flutter/packages/runanywhere/pubspec.yaml
  • sdk/runanywhere-flutter/packages/runanywhere/test/vlm_file_resolution_test.dart
  • sdk/runanywhere-flutter/packages/runanywhere/windows/CMakeLists.txt
  • sdk/runanywhere-flutter/packages/runanywhere/windows/include/runanywhere/runanywhere_plugin_c_api.h
  • sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin.cpp
  • sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin.h
  • sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin_c_api.cpp
  • sdk/runanywhere-flutter/packages/runanywhere_llamacpp/lib/native/llamacpp_bindings.dart
  • sdk/runanywhere-flutter/packages/runanywhere_llamacpp/pubspec.yaml
  • sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/CMakeLists.txt
  • sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/include/runanywhere_llamacpp/runanywhere_llamacpp_plugin_c_api.h
  • sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.cpp
  • sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.h
  • sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin_c_api.cpp
  • sdk/runanywhere-flutter/packages/runanywhere_onnx/lib/native/onnx_bindings.dart
  • sdk/runanywhere-flutter/packages/runanywhere_onnx/pubspec.yaml
  • sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/CMakeLists.txt
  • sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/include/runanywhere_onnx/runanywhere_onnx_plugin_c_api.h
  • sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin.cpp
  • sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin.h
  • sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin_c_api.cpp
💤 Files with no reviewable changes (1)
  • examples/flutter/RunAnywhereAI/.gitignore

Comment thread examples/flutter/RunAnywhereAI/lib/core/models/proxy_settings.dart
Comment on lines +79 to +85
final proxyDirective = switch (settings.scheme) {
ProxyScheme.socks5 => 'SOCKS5 ${settings.host.trim()}:${settings.port}',
ProxyScheme.http || ProxyScheme.https =>
'PROXY ${settings.host.trim()}:${settings.port}',
};

client.findProxy = (_) => proxyDirective;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Does dart:io HttpClient.findProxy support SOCKS5 proxy directives?

💡 Result:

No, dart:io HttpClient.findProxy does not support SOCKS5 proxy directives. The findProxy callback returns strings in PAC format, limited to "DIRECT" or "PROXY host:port", which dart:io interprets as HTTP/HTTPS CONNECT proxies only. SOCKS5 is not natively supported. Official Dart API docs confirm findProxy expects "PROXY host:port" format with no mention of SOCKS protocols. findProxyFromEnvironment parses environment variables like http_proxy into this HTTP-only format, defaulting port 1080 but treating as HTTP proxy. Open Dart SDK issue #42920 (since 2020) requests SOCKS support, confirming absence. Users must implement custom connectionFactory for SOCKS5 using third-party packages like socks5_proxy. Native support lacks as of 2026-04-23; recent docs and issues show no change.

Citations:


🏁 Script executed:

cat -n examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart | sed -n '70,95p'

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 1121


🏁 Script executed:

cat -n examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart | sed -n '65,105p'

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 1508


SOCKS5 is not supported by HttpClient.findProxy.

dart:io's HttpClient.findProxy only understands DIRECT and PROXY host:port directives (HTTP CONNECT-style proxies). Returning 'SOCKS5 host:port' from findProxy is not recognized and will result in the client attempting to treat SOCKS5 as an unknown directive — at best falling back in unintended ways, at worst throwing. If SOCKS5 support is required, route the connection via a package like socks5_proxy (e.g. SocksTCPClient.connect / Socks5HttpOverrides) or use a custom connectionFactory rather than relying on findProxy.

Also, the ProxyScheme.https branch returns a plain PROXY host:port, which establishes a plaintext TCP connection to the proxy; if the user intends a TLS connection to the proxy itself, this path does not deliver that. Consider either dropping the https scheme from ProxyScheme or documenting that it is equivalent to http.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart`
around lines 79 - 85, The current client.findProxy handler returns a 'SOCKS5
...' directive and treats ProxyScheme.https as plain 'PROXY', but dart:io
HttpClient.findProxy only supports DIRECT and PROXY host:port; update the logic
in the proxyDirective / client.findProxy handling so that for ProxyScheme.socks5
you do not return a SOCKS5 directive but instead route via a proper SOCKS5
mechanism (e.g., use a Socks5 package or implement a custom
connectionFactory/HttpOverrides) and for ProxyScheme.https either document/clear
that it is equivalent to plaintext PROXY or implement TLS-to-proxy by using a
custom connectionFactory that performs a TLS handshake to the proxy; adjust
references in the code around settings.scheme, ProxyScheme.socks5,
ProxyScheme.http/https and client.findProxy to use the chosen approach.

Comment thread examples/flutter/RunAnywhereAI/lib/core/services/proxy_settings_service.dart Outdated
Comment on lines 116 to +124
onModelSelected: (model) async {
await _loadModel(model);
await _refreshModelState();
},
),
));
}

/// Load STT model using RunAnywhere SDK directly (matches Swift STTViewModel pattern)
Future<void> _loadModel(ModelInfo model) async {
Future<void> _refreshModelState() async {
final currentModel = await sdk.RunAnywhere.currentSTTModel();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Verify whether ModelSelectionSheet loads STT models before invoking onModelSelected.
set -euo pipefail

fd -i '^model_selection_sheet\.dart$' | while read -r file; do
  printf '\n== %s ==\n' "$file"
  rg -n -C5 'onModelSelected|loadSTTModel|currentSTTModel|ModelSelectionContext\.stt' "$file" || true
done

printf '\n== Other STT load/current call sites ==\n'
rg -n -C3 '\b(loadSTTModel|currentSTTModel)\s*\(' --type=dart || true

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 7356


🏁 Script executed:

cat -n examples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dart | sed -n '110,135p'

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 1077


🏁 Script executed:

cat -n examples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dart | grep -A8 -B2 'TTS\|tts\|onModelSelected'

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 470


🏁 Script executed:

wc -l examples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dart

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 152


Load the selected STT model in the callback before refreshing SDK state.

The callback ignores the model parameter and only refreshes the UI state of what's currently loaded. Since _refreshModelState() calls sdk.RunAnywhere.currentSTTModel() without first loading the selected model, the view will continue displaying the previous or null model and transcription will fail.

Add await sdk.RunAnywhere.loadSTTModel(model.id); before await _refreshModelState(); in the onModelSelected callback (lines 116–118).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dart`
around lines 116 - 124, In the onModelSelected callback, first call and await
sdk.RunAnywhere.loadSTTModel(model.id) to load the newly selected STT model,
then call await _refreshModelState(); so the UI reads the updated model; update
the onModelSelected handler in the widget to await
sdk.RunAnywhere.loadSTTModel(model.id) before invoking _refreshModelState();
keep the existing _refreshModelState which calls
sdk.RunAnywhere.currentSTTModel().

Comment thread examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp
Comment thread sdk/runanywhere-commons/scripts/build-windows.bat Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_auth.dart (3)

143-156: ⚠️ Potential issue | 🟠 Major

Do not log auth request or response bodies.

Line 144 can log the API key, and Line 156 can log access/refresh tokens. Keep only non-sensitive metadata.

🔒 Proposed log redaction
       _logger.debug('Auth POST to: $url');
-      _logger.debug('Auth body: $requestJson');
+      _logger.debug('Auth request prepared', metadata: {
+        'bodyLength': requestJson.length,
+      });
 
       final response = await http.post(
         url,
         headers: {
           'Content-Type': 'application/json',
@@
       );
 
       _logger.debug('Auth response status: ${response.statusCode}');
-      _logger.debug('Auth response body: ${response.body}');
+      _logger.debug('Auth response received', metadata: {
+        'bodyLength': response.body.length,
+      });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_auth.dart`
around lines 143 - 156, The code currently logs sensitive auth payloads; remove
or stop logging the request/response bodies and any raw tokens by deleting the
_logger.debug calls that reference requestJson and response.body and avoid
printing url if it contains credentials; keep only non-sensitive metadata such
as the endpoint path or host and response.statusCode (the existing
_logger.debug('Auth response status: ${response.statusCode}') is fine); update
references in this block (the variables requestJson, response.body, url and the
logger calls) so no API keys, access tokens, refresh tokens, or full JSON bodies
are written to logs.

783-805: ⚠️ Potential issue | 🟡 Minor

Write UTF-8 bytes into the Pointer<Utf8> buffer.

value.codeUnits returns UTF-16 code units, but the output buffer expects UTF-8 bytes. This causes non-ASCII characters to be corrupted. The dart:convert import is available; use utf8.encode(value) instead.

🛠️ Proposed encoding fix
     if (value == null) return -1;
 
     // Copy to output buffer
-    final bytes = value.codeUnits;
+    final bytes = utf8.encode(value);
     final maxLen = bufferSize - 1; // Leave room for null terminator
 
     if (bytes.length > maxLen) {
       return -1; // Buffer too small
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_auth.dart`
around lines 783 - 805, In _secureRetrieveCallback replace the UTF-16 usage with
UTF-8: compute bytes with utf8.encode(value) (dart:convert is already
available), use that bytes.length when checking against bufferSize and when
copying into outValue.cast<Uint8>(), then write the null terminator; this
ensures non-ASCII characters are encoded correctly for the Pointer<Utf8> output
buffer.

410-435: ⚠️ Potential issue | 🟠 Major

Use the configured SDK environment in the native auth request.

Line 429 always sends environment 0, hardcoding development mode regardless of the configured _environment value. This causes staging/production initialization to be ignored for rac_auth_build_authenticate_request.

The native environment values are confirmed in rac_environment.h:

  • RAC_ENV_DEVELOPMENT = 0
  • RAC_ENV_STAGING = 1
  • RAC_ENV_PRODUCTION = 2

Add this mapping function to DartBridgeAuth:

Proposed fix
+  static int _environmentToNativeValue(SDKEnvironment environment) {
+    switch (environment) {
+      case SDKEnvironment.development:
+        return 0;
+      case SDKEnvironment.staging:
+        return 1;
+      case SDKEnvironment.production:
+        return 2;
+    }
+  }

Then update line 429:

-        config.ref.environment = 0;
+        config.ref.environment = _environmentToNativeValue(_environment);
         config.ref.apiKey = apiKeyPtr;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_auth.dart`
around lines 410 - 435, The code in _buildAuthenticateRequestJSON currently
hardcodes config.ref.environment = 0 which forces development mode; update
DartBridgeAuth to map the instance _environment value to the native rac
environment ints (RAC_ENV_DEVELOPMENT=0, RAC_ENV_STAGING=1,
RAC_ENV_PRODUCTION=2) and assign that mapped int to config.ref.environment
before calling rac_auth_build_authenticate_request; add a small helper method on
DartBridgeAuth (e.g., _nativeEnvFor(_environment) or mapEnvironmentToNative)
that returns the correct int and use it when setting config.ref.environment on
the RacSdkConfigStruct.
🧹 Nitpick comments (1)
examples/flutter/RunAnywhereAI/test/features/dropdown_form_field_compatibility_test.dart (1)

1-38: Exercise dropdown selection, not just initial rendering.

These tests would still pass if the dropdown stopped syncing selection state. Please add interaction assertions that open each selector, choose a non-default option, and verify downstream state/callback output.

🧪 Example test expansion
 import 'package:flutter/material.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:runanywhere_ai/features/models/add_model_from_url_view.dart';
+import 'package:runanywhere_ai/features/models/model_types.dart';
 import 'package:runanywhere_ai/features/structured_output/structured_output_view.dart';
 
 void main() {
   testWidgets('StructuredOutputView builds with example selector',
       (tester) async {
@@
 
     expect(find.byType(StructuredOutputView), findsOneWidget);
     expect(find.byType(DropdownButtonFormField<int>), findsOneWidget);
     expect(find.text('Recipe'), findsOneWidget);
+
+    await tester.tap(find.byType(DropdownButtonFormField<int>));
+    await tester.pumpAndSettle();
+    await tester.tap(find.text('Weather').last);
+    await tester.pumpAndSettle();
+
+    final promptField = tester.widget<TextField>(find.byType(TextField));
+    expect(promptField.controller?.text, 'What is the weather in Paris?');
   });
 
   testWidgets('AddModelFromURLView builds with framework selector',
       (tester) async {
+    ModelInfo? addedModel;
+
     await tester.pumpWidget(
       MaterialApp(
         home: Scaffold(
           body: AddModelFromURLView(
-            onModelAdded: (_) {},
+            onModelAdded: (model) => addedModel = model,
           ),
         ),
       ),
     );
@@
     expect(find.byType(AddModelFromURLView), findsOneWidget);
     expect(find.text('Framework'), findsOneWidget);
     expect(find.text('Target Framework'), findsOneWidget);
+
+    await tester.tap(find.byType(DropdownButtonFormField<LLMFramework>));
+    await tester.pumpAndSettle();
+    await tester.tap(find.text(LLMFramework.mediaPipe.displayName).last);
+    await tester.pumpAndSettle();
+
+    await tester.enterText(find.byType(TextFormField).at(0), 'Test Model');
+    await tester.enterText(
+      find.byType(TextFormField).at(1),
+      'https://example.com/model.gguf',
+    );
+    await tester.tap(find.text('Add Model'));
+    await tester.pump();
+
+    expect(addedModel?.preferredFramework, LLMFramework.mediaPipe);
+    expect(addedModel?.compatibleFrameworks, contains(LLMFramework.mediaPipe));
   });
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@examples/flutter/RunAnywhereAI/test/features/dropdown_form_field_compatibility_test.dart`
around lines 1 - 38, Update the two widget tests to interact with the dropdowns
instead of only asserting initial render: use tester.tap(...) and
tester.pumpAndSettle() to open the DropdownButtonFormField<int> inside
StructuredOutputView and AddModelFromURLView, tap a non-default DropdownMenuItem
label (e.g., a second option like 'Example B' or a different framework string),
then pump and assert that the new selection text is visible and any downstream
state or callbacks update (for AddModelFromURLView verify the onModelAdded or a
visible target framework field changes). If the specific dropdowns are
ambiguous, add unique Keys to the DropdownButtonFormField instances in
StructuredOutputView and AddModelFromURLView to target them reliably from the
tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_auth.dart`:
- Around line 143-156: The code currently logs sensitive auth payloads; remove
or stop logging the request/response bodies and any raw tokens by deleting the
_logger.debug calls that reference requestJson and response.body and avoid
printing url if it contains credentials; keep only non-sensitive metadata such
as the endpoint path or host and response.statusCode (the existing
_logger.debug('Auth response status: ${response.statusCode}') is fine); update
references in this block (the variables requestJson, response.body, url and the
logger calls) so no API keys, access tokens, refresh tokens, or full JSON bodies
are written to logs.
- Around line 783-805: In _secureRetrieveCallback replace the UTF-16 usage with
UTF-8: compute bytes with utf8.encode(value) (dart:convert is already
available), use that bytes.length when checking against bufferSize and when
copying into outValue.cast<Uint8>(), then write the null terminator; this
ensures non-ASCII characters are encoded correctly for the Pointer<Utf8> output
buffer.
- Around line 410-435: The code in _buildAuthenticateRequestJSON currently
hardcodes config.ref.environment = 0 which forces development mode; update
DartBridgeAuth to map the instance _environment value to the native rac
environment ints (RAC_ENV_DEVELOPMENT=0, RAC_ENV_STAGING=1,
RAC_ENV_PRODUCTION=2) and assign that mapped int to config.ref.environment
before calling rac_auth_build_authenticate_request; add a small helper method on
DartBridgeAuth (e.g., _nativeEnvFor(_environment) or mapEnvironmentToNative)
that returns the correct int and use it when setting config.ref.environment on
the RacSdkConfigStruct.

---

Nitpick comments:
In
`@examples/flutter/RunAnywhereAI/test/features/dropdown_form_field_compatibility_test.dart`:
- Around line 1-38: Update the two widget tests to interact with the dropdowns
instead of only asserting initial render: use tester.tap(...) and
tester.pumpAndSettle() to open the DropdownButtonFormField<int> inside
StructuredOutputView and AddModelFromURLView, tap a non-default DropdownMenuItem
label (e.g., a second option like 'Example B' or a different framework string),
then pump and assert that the new selection text is visible and any downstream
state or callbacks update (for AddModelFromURLView verify the onModelAdded or a
visible target framework field changes). If the specific dropdowns are
ambiguous, add unique Keys to the DropdownButtonFormField instances in
StructuredOutputView and AddModelFromURLView to target them reliably from the
tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b2fe9315-691f-41b1-98d8-a8a9a17080f9

📥 Commits

Reviewing files that changed from the base of the PR and between 7df8df2 and 1a58637.

📒 Files selected for processing (5)
  • examples/flutter/RunAnywhereAI/lib/features/models/add_model_from_url_view.dart
  • examples/flutter/RunAnywhereAI/lib/features/structured_output/structured_output_view.dart
  • examples/flutter/RunAnywhereAI/test/features/dropdown_form_field_compatibility_test.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_auth.dart
  • sdk/runanywhere-flutter/packages/runanywhere/test/auth_sdk_config_ffi_layout_test.dart

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 10

♻️ Duplicate comments (2)
sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart (1)

238-269: ⚠️ Potential issue | 🟠 Major

Close the file sink when streaming fails or cancellation interrupts the loop.

If response.stream throws, or client.close() interrupts the download, execution skips sink.flush()/sink.close(). That can leave partial files open, which is particularly painful on Windows.

Ensure the sink is always closed
               final file = File(destPath);
               await file.create(recursive: true);
               final sink = file.openWrite();
               var downloaded = 0;
 
-              await for (final chunk in response.stream) {
-                sink.add(chunk);
-                downloaded += chunk.length;
-
-                final overallProgress =
-                    _calculateOverallMultiFileDownloadProgress(
-                  completedFiles: i,
-                  totalFiles: totalFiles,
-                  downloadedBytesForCurrentFile: downloaded,
-                  totalBytesForCurrentFile: currentFileTotalBytes,
-                );
-                yield ModelDownloadProgress(
-                  modelId: modelId,
-                  bytesDownloaded: cumulativeDownloaded + downloaded,
-                  totalBytes: totalModelBytes,
-                  stage: ModelDownloadStage.downloading,
-                  overallProgress: overallProgress * 0.9,
-                );
+              try {
+                await for (final chunk in response.stream) {
+                  sink.add(chunk);
+                  downloaded += chunk.length;
+
+                  final overallProgress =
+                      _calculateOverallMultiFileDownloadProgress(
+                    completedFiles: i,
+                    totalFiles: totalFiles,
+                    downloadedBytesForCurrentFile: downloaded,
+                    totalBytesForCurrentFile: currentFileTotalBytes,
+                  );
+                  yield ModelDownloadProgress(
+                    modelId: modelId,
+                    bytesDownloaded: cumulativeDownloaded + downloaded,
+                    totalBytes: totalModelBytes,
+                    stage: ModelDownloadStage.downloading,
+                    overallProgress: overallProgress * 0.9,
+                  );
+                }
+
+                await sink.flush();
+              } finally {
+                await sink.close();
               }
-
-              await sink.flush();
-              await sink.close();
               cumulativeDownloaded += downloaded;
               _logger.info('Downloaded: ${descriptor.destinationPath}');
             } finally {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart`
around lines 238 - 269, The file sink opened with File(destPath).openWrite() can
remain unclosed if response.stream throws or cancellation occurs; modify the
download routine so the sink is always closed: wrap the streaming loop and
subsequent flush in a try/finally that guarantees await sink.flush() and await
sink.close() (or use sink.close() in the finally) after the await for (final
chunk in response.stream) block; ensure this applies around the sink variable
created in the method (the openWrite() call assigned to sink) so that even when
client.close() or response.stream throws, the sink is closed before removing
entries from _activeDownloads or returning from the method.
examples/flutter/RunAnywhereAI/lib/core/models/proxy_settings.dart (1)

21-24: ⚠️ Potential issue | 🟠 Major

Don’t advertise HTTPS proxy support unless TLS-to-proxy is implemented.

ProxyScheme.https remains in supportedValues, but ExampleHttpService._proxyDirectiveForSettings maps it to the same plain PROXY host:port directive as HTTP. Users selecting HTTPS will not get a TLS connection to the proxy itself; either remove HTTPS from supported schemes for now or implement a custom connection path that actually negotiates TLS with the proxy.

🐛 Minimal fix if HTTPS proxy transport is not supported yet
-  bool get isSupportedInExampleApp => this != ProxyScheme.socks5;
+  bool get isSupportedInExampleApp => this == ProxyScheme.http;

Please verify the Dart proxy directive semantics:

Does dart:io HttpClient.findProxy support HTTPS proxy transport, or only DIRECT and PROXY host:port PAC directives?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/lib/core/models/proxy_settings.dart` around
lines 21 - 24, The enum value ProxyScheme.https is being advertised as supported
but ExampleHttpService._proxyDirectiveForSettings does not establish
TLS-to-proxy; either remove HTTPS from the advertised set or implement TLS proxy
transport. Fix option A (preferred short-term): change the
isSupportedInExampleApp/supportedValues logic to explicitly exclude
ProxyScheme.https so HTTPS is not listed as supported. Fix option B (long-term):
implement proper TLS-to-proxy handling in
ExampleHttpService._proxyDirectiveForSettings so when ProxyScheme.https is
chosen you perform a TLS handshake/CONNECT to the proxy rather than emitting a
plain "PROXY host:port" directive. Reference symbols: ProxyScheme,
isSupportedInExampleApp, supportedValues, and
ExampleHttpService._proxyDirectiveForSettings.
🧹 Nitpick comments (5)
sdk/runanywhere-flutter/packages/runanywhere/test/dart_bridge_auth_test.dart (1)

60-61: Redaction assertions are weak — consider checking substring presence across string values.

Iterable.contains performs equality matching, so these expectations only fail if a metadata value is exactly equal to the full requestJson/response.body. Since the metadata map only holds host/path/lengths/status, these checks trivially pass even if a future change started leaking a prefix or substring of the body. A stricter assertion would iterate string values and check that none contain sensitive tokens.

🔧 Suggested stronger redaction check
-    expect(requestMetadata.values, isNot(contains(requestJson)));
-    expect(responseMetadata.values, isNot(contains(response.body)));
+    for (final v in requestMetadata.values.whereType<String>()) {
+      expect(v.contains('secret-key'), isFalse);
+      expect(v.contains('device-123'), isFalse);
+    }
+    for (final v in responseMetadata.values.whereType<String>()) {
+      expect(v.contains('secret-token'), isFalse);
+      expect(v.contains('another-secret'), isFalse);
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-flutter/packages/runanywhere/test/dart_bridge_auth_test.dart`
around lines 60 - 61, The current assertions use Iterable.contains which only
checks full equality and can miss partial leaks; update the tests that reference
requestMetadata, responseMetadata, requestJson and response.body to assert that
no metadata string value contains those payloads as substrings (e.g., iterate
over requestMetadata.values/responseMetadata.values and assert none.any/none
where the value.contains(requestJson) or value.contains(response.body)). Locate
the expectations around expect(requestMetadata.values, ...) and
expect(responseMetadata.values, ...) and replace them with substring-based
checks so any partial leakage of requestJson or response.body fails the test.
examples/flutter/RunAnywhereAI/pubspec.yaml (2)

52-53: Exact pin on camera_windows — intentional, but consider a caret constraint.

camera_windows: 0.2.6+1 is an exact pin, which prevents picking up patch fixes. The adjacent comment clarifies the Dart-3.3 compatibility intent, which is fine; if you want non-breaking patches while still holding the line on Dart compat, ^0.2.6 (pre-1.0 semantics in pub: patch updates only within 0.2.x) would be a reasonable alternative. Not a blocker.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/pubspec.yaml` around lines 52 - 53, The
pubspec currently pins camera_windows exactly to "0.2.6+1" which blocks
receiving patch fixes; change the dependency entry for camera_windows in
pubspec.yaml from the exact pin to a caret constraint like "^0.2.6" to allow
non-breaking patch updates while preserving Dart 3.3 compatibility. Locate the
camera_windows line and update the version string accordingly, then run pub get
to verify dependency resolution.

76-77: Override is redundant with the direct path dependency.

Since flutter_secure_storage_windows on lines 36-37 already points at the vendored path, the matching entry in dependency_overrides is only strictly needed if some transitive dependency (e.g., flutter_secure_storage 9.x) pulls a different version of this package. If that's the intent (likely, given the comment on line 68), the entry is correct — but worth a brief inline comment explaining it's there to win over the transitive pin from flutter_secure_storage, not to override the direct declaration.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/pubspec.yaml` around lines 76 - 77, The
dependency_overrides entry for flutter_secure_storage_windows is redundant with
the direct path dependency unless it's intended to beat a transitive pin from
flutter_secure_storage; update the pubspec.yaml to add a brief inline comment
next to the dependency_overrides entry explaining that
flutter_secure_storage_windows is being overridden to win over a transitive pin
from the flutter_secure_storage package (e.g., to force the vendored path), and
reference the flutter_secure_storage_windows and dependency_overrides
identifiers so future readers understand the purpose.
sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart (1)

155-164: Document that factory-created clients are owned and closed here.

downloadModel closes each returned client later, so callers must not return a shared/singleton client from this factory. A short ownership contract avoids accidental proxy/client reuse breakage.

Clarify factory ownership
+  /// Configures the HTTP client factory used for model downloads.
+  ///
+  /// The returned client is owned by [ModelDownloadService] and will be closed
+  /// after the file download completes. Factories should return a fresh client
+  /// per invocation, not a shared singleton.
   void configureClientFactory(DownloadHttpClientFactory? factory) {
     _clientFactory = factory;
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart`
around lines 155 - 164, Update the public API docs to state that any http.Client
returned by the configured factory is owned and will be closed by this class
(the DownloadService) when done; specifically document this ownership contract
on configureClientFactory (and on the factory typedef if present) so
implementers of the factory passed into configureClientFactory and users of
_createClient know not to return shared/singleton clients (downloadModel will
close each returned client). Ensure the doc string mentions that the service
will close the client and callers must return a fresh/disposable client for each
call.
sdk/runanywhere-flutter/packages/runanywhere/test/download_service_test.dart (1)

29-44: Add one assertion for rounded per-file estimates.

The helper in sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart:19-24 uses ceil(), so a non-even division case would prevent regressions to floor/truncating behavior.

Test coverage addition
   test('estimates per-file size from total model size when needed', () {
     expect(
       estimatePerFileDownloadSizeForTesting(
         totalModelBytes: 300,
         totalFiles: 3,
       ),
       100,
     );
+    expect(
+      estimatePerFileDownloadSizeForTesting(
+        totalModelBytes: 301,
+        totalFiles: 3,
+      ),
+      101,
+    );
     expect(
       estimatePerFileDownloadSizeForTesting(
         totalModelBytes: null,
         totalFiles: 3,
       ),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-flutter/packages/runanywhere/test/download_service_test.dart`
around lines 29 - 44, Add a test asserting that
estimatePerFileDownloadSizeForTesting uses ceiling for non-even divisions: call
estimatePerFileDownloadSizeForTesting(totalModelBytes: 301, totalFiles: 3) and
expect 101 to catch any regressions to floor/truncate; update the test block
that already checks 300/3 and null case to include this extra expect (the helper
uses ceil() in download_service.dart via estimatePerFileDownloadSizeForTesting).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart`:
- Around line 27-55: The proxied request operations in getJson and testProxy can
hang after TCP connect; update both getJson and testProxy (which use
createScopedHttpClient, request.getUrl, request.close, and
response.transform/join) to apply end-to-end timeouts: wrap the request.close()
Future (and in getJson also the body read response.transform(...).join()) with
.timeout(a reasonable Duration) and handle TimeoutException to return a failed
ExampleHttpResponse or false respectively, ensuring client.close(force: true)
still runs in the finally block; use a shared Duration constant for the timeout
to keep behavior consistent across both methods.

In
`@examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart`:
- Around line 1125-1133: The Test button currently always calls
_testProxyConfiguration(scope) which can show "proxy test succeeded" even when
the proxy scope is disabled or incomplete because
ExampleHttpService.createScopedHttpClient falls back to DIRECT; update the UI
and handler so the button is disabled (onPressed == null) when the provided
scope is disabled or missing required fields (e.g., check scope.enabled or
scope.isEnabled and required host/port/auth fields), and/or make
_testProxyConfiguration(scope) return early with an appropriate message if
scope.enabled is false or validation fails; locate the button widget and the
_testProxyConfiguration function and add the guard/validation to prevent running
the test when the proxy is not actually configured.
- Around line 240-416: The TextEditingController instances created in
_showProxyConfigDialog (hostController, portController, usernameController,
passwordController) are not disposed and leak; wrap the await showDialog(...)
call in a try/finally inside _showProxyConfigDialog so that in the finally block
you call dispose() on each controller (hostController.dispose(),
portController.dispose(), usernameController.dispose(),
passwordController.dispose()) to ensure they are always cleaned up after the
dialog closes or an error occurs.

In `@examples/flutter/RunAnywhereAI/pubspec.yaml`:
- Around line 35-37: The pubspec lists flutter_secure_storage: ^9.0.0 but uses a
vendored flutter_secure_storage_windows (v3.1.2) that requires
flutter_secure_storage_platform_interface ^1.1.1, causing a platform_interface
version conflict; fix by either (preferred) updating the vendored
flutter_secure_storage_windows to a 4.x+ release that supports
flutter_secure_storage_platform_interface ^2.0.0 (replace the vendor folder or
point the dependency to a compatible 4.x source), or remove the vendored
flutter_secure_storage_windows override so pub can resolve the transitive
4.x-compatible Windows package, or alternatively downgrade
flutter_secure_storage to a 8.x release that matches platform_interface
^1.1.1—apply the chosen change in pubspec.yaml and run flutter pub get to verify
resolution.

In `@examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp`:
- Around line 147-149: OnCreate() can fail but the code currently returns false
without destroying the HWND created earlier; update the Create() flow so that
after calling UpdateTheme(window) you call OnCreate() and if it returns false
you call ::DestroyWindow(window) (and clear the stored HWND/member window
handle) before returning false, ensuring the native window is torn down when
OnCreate() fails.
- Around line 224-231: The Destroy method currently ignores DestroyWindow's BOOL
result and unconditionally clears window_handle_ and potentially unregisters the
class; update Win32Window::Destroy to capture the return value of
DestroyWindow(window_handle_), only set window_handle_ to nullptr if
DestroyWindow succeeds, and only decrement/consider g_active_window_count and
call WindowClassRegistrar::GetInstance()->UnregisterWindowClass() when
destruction succeeded; if DestroyWindow fails, preserve window_handle_ (so
callers can retry or cleanup later) and surface/log the failure (e.g., via
GetLastError) rather than proceeding to unregister the class.
- Around line 181-221: The switch falls through to DefWindowProc using the
cleared member window_handle_; change the final call to use the incoming hwnd
parameter (call DefWindowProc(hwnd, message, wparam, lparam)) and add a
WM_NCDESTROY case that clears the per-window user data (SetWindowLongPtr(hwnd,
GWLP_USERDATA, 0)) and performs any final cleanup after WM_DESTROY (e.g., ensure
window_handle_ is already nulled and return 0), so default processing gets the
correct hwnd and GWLP_USERDATA is cleared when the non-client destroy message
arrives.

In `@sdk/runanywhere-commons/scripts/build-windows.bat`:
- Around line 100-106: The current Windows batch loop that copies Sherpa ONNX
DLLs (the block gated by BUILD_ONNX) swallows copy failures by redirecting
output to nul; change it so each copy checks the copy exit status (ERRORLEVEL)
after the copy of each file from
"%ROOT%\third_party\sherpa-onnx-windows\bin\*.dll" and
"%ROOT%\third_party\sherpa-onnx-windows\lib\*.dll" and, on failure, emit an
error to the console and exit the script with non‑zero status (e.g., exit /b 1)
so the build fails instead of reporting “Build complete” with missing DLLs.
Ensure you apply this check inside both FOR loops that perform the copy to
"%DIST_DIR%\".
- Around line 8-16: The argument parsing currently silently ignores unknown
options, so update the parse logic around the if "%~1" checks (the parse_args
loop / parse_args label) to reject unrecognized arguments: when none of the
known branches (all, llamacpp, onnx, --clean, -h, --help) match, print a clear
error mentioning the bad token and call the existing usage flow (goto usage) or
exit /b 1; ensure variables like BACKENDS and CLEAN are only set when recognized
and that the parser halts with non-zero status for unknown options to prevent
accidental successful builds with wrong defaults.

In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart`:
- Around line 203-215: The multi-file download currently treats empty or
partially missing descriptors as success; before calling _updateModelLocalPath
or emitting downloadCompleted, validate the manifest and abort on invalid data:
if multiFile.files.isEmpty, log an error via _logger.error and emit a failure
progress (e.g. ModelDownloadProgress.failed or throw) and return; likewise
inside the loop if descriptor.url == null, immediately log the error, emit a
failure and stop the download rather than continue to the next file. Ensure
neither _updateModelLocalPath nor the final downloadCompleted progress are
called when the manifest is invalid.

---

Duplicate comments:
In `@examples/flutter/RunAnywhereAI/lib/core/models/proxy_settings.dart`:
- Around line 21-24: The enum value ProxyScheme.https is being advertised as
supported but ExampleHttpService._proxyDirectiveForSettings does not establish
TLS-to-proxy; either remove HTTPS from the advertised set or implement TLS proxy
transport. Fix option A (preferred short-term): change the
isSupportedInExampleApp/supportedValues logic to explicitly exclude
ProxyScheme.https so HTTPS is not listed as supported. Fix option B (long-term):
implement proper TLS-to-proxy handling in
ExampleHttpService._proxyDirectiveForSettings so when ProxyScheme.https is
chosen you perform a TLS handshake/CONNECT to the proxy rather than emitting a
plain "PROXY host:port" directive. Reference symbols: ProxyScheme,
isSupportedInExampleApp, supportedValues, and
ExampleHttpService._proxyDirectiveForSettings.

In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart`:
- Around line 238-269: The file sink opened with File(destPath).openWrite() can
remain unclosed if response.stream throws or cancellation occurs; modify the
download routine so the sink is always closed: wrap the streaming loop and
subsequent flush in a try/finally that guarantees await sink.flush() and await
sink.close() (or use sink.close() in the finally) after the await for (final
chunk in response.stream) block; ensure this applies around the sink variable
created in the method (the openWrite() call assigned to sink) so that even when
client.close() or response.stream throws, the sink is closed before removing
entries from _activeDownloads or returning from the method.

---

Nitpick comments:
In `@examples/flutter/RunAnywhereAI/pubspec.yaml`:
- Around line 52-53: The pubspec currently pins camera_windows exactly to
"0.2.6+1" which blocks receiving patch fixes; change the dependency entry for
camera_windows in pubspec.yaml from the exact pin to a caret constraint like
"^0.2.6" to allow non-breaking patch updates while preserving Dart 3.3
compatibility. Locate the camera_windows line and update the version string
accordingly, then run pub get to verify dependency resolution.
- Around line 76-77: The dependency_overrides entry for
flutter_secure_storage_windows is redundant with the direct path dependency
unless it's intended to beat a transitive pin from flutter_secure_storage;
update the pubspec.yaml to add a brief inline comment next to the
dependency_overrides entry explaining that flutter_secure_storage_windows is
being overridden to win over a transitive pin from the flutter_secure_storage
package (e.g., to force the vendored path), and reference the
flutter_secure_storage_windows and dependency_overrides identifiers so future
readers understand the purpose.

In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart`:
- Around line 155-164: Update the public API docs to state that any http.Client
returned by the configured factory is owned and will be closed by this class
(the DownloadService) when done; specifically document this ownership contract
on configureClientFactory (and on the factory typedef if present) so
implementers of the factory passed into configureClientFactory and users of
_createClient know not to return shared/singleton clients (downloadModel will
close each returned client). Ensure the doc string mentions that the service
will close the client and callers must return a fresh/disposable client for each
call.

In
`@sdk/runanywhere-flutter/packages/runanywhere/test/dart_bridge_auth_test.dart`:
- Around line 60-61: The current assertions use Iterable.contains which only
checks full equality and can miss partial leaks; update the tests that reference
requestMetadata, responseMetadata, requestJson and response.body to assert that
no metadata string value contains those payloads as substrings (e.g., iterate
over requestMetadata.values/responseMetadata.values and assert none.any/none
where the value.contains(requestJson) or value.contains(response.body)). Locate
the expectations around expect(requestMetadata.values, ...) and
expect(responseMetadata.values, ...) and replace them with substring-based
checks so any partial leakage of requestJson or response.body fails the test.

In
`@sdk/runanywhere-flutter/packages/runanywhere/test/download_service_test.dart`:
- Around line 29-44: Add a test asserting that
estimatePerFileDownloadSizeForTesting uses ceiling for non-even divisions: call
estimatePerFileDownloadSizeForTesting(totalModelBytes: 301, totalFiles: 3) and
expect 101 to catch any regressions to floor/truncate; update the test block
that already checks 300/3 and null case to include this extra expect (the helper
uses ceil() in download_service.dart via estimatePerFileDownloadSizeForTesting).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: eb9c1fcc-a63e-48b0-92dd-826067395d11

📥 Commits

Reviewing files that changed from the base of the PR and between 1a58637 and 67826b3.

📒 Files selected for processing (20)
  • examples/flutter/RunAnywhereAI/lib/core/models/proxy_settings.dart
  • examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart
  • examples/flutter/RunAnywhereAI/lib/core/services/proxy_settings_service.dart
  • examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart
  • examples/flutter/RunAnywhereAI/pubspec.yaml
  • examples/flutter/RunAnywhereAI/test/core/models/proxy_settings_test.dart
  • examples/flutter/RunAnywhereAI/test/core/services/example_http_service_test.dart
  • examples/flutter/RunAnywhereAI/test/core/services/proxy_settings_service_test.dart
  • examples/flutter/RunAnywhereAI/test/features/dropdown_form_field_compatibility_test.dart
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/pubspec.yaml
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/pubspec.yaml
  • examples/flutter/RunAnywhereAI/windows/runner/flutter_window.cpp
  • examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp
  • sdk/runanywhere-commons/scripts/build-windows.bat
  • sdk/runanywhere-commons/src/backends/onnx/rac_backend_onnx_register.cpp
  • sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_auth.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/native/platform_loader.dart
  • sdk/runanywhere-flutter/packages/runanywhere/test/dart_bridge_auth_test.dart
  • sdk/runanywhere-flutter/packages/runanywhere/test/download_service_test.dart
✅ Files skipped from review due to trivial changes (3)
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/pubspec.yaml
  • examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/pubspec.yaml
  • sdk/runanywhere-commons/src/backends/onnx/rac_backend_onnx_register.cpp
🚧 Files skipped from review as they are similar to previous changes (4)
  • sdk/runanywhere-flutter/packages/runanywhere/lib/native/platform_loader.dart
  • examples/flutter/RunAnywhereAI/windows/runner/flutter_window.cpp
  • examples/flutter/RunAnywhereAI/test/features/dropdown_form_field_compatibility_test.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_auth.dart

Comment on lines +27 to +55
Future<ExampleHttpResponse> getJson(Uri uri) async {
final client = await createScopedHttpClient(ProxyScope.general, uri);

try {
final request = await client.getUrl(uri);
request.headers.set(HttpHeaders.acceptHeader, 'application/json');

final response = await request.close();
final body = await response.transform(utf8.decoder).join();

return ExampleHttpResponse(
statusCode: response.statusCode,
body: body,
);
} finally {
client.close(force: true);
}
}

Future<bool> testProxy(ProxyScope scope, Uri uri) async {
final client = await createScopedHttpClient(scope, uri);
try {
final request = await client.getUrl(uri);
request.headers.set(HttpHeaders.acceptHeader, 'application/json');
final response = await request.close();
return response.statusCode >= 200 && response.statusCode < 300;
} finally {
client.close(force: true);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Does Dart HttpClient.connectionTimeout cover only TCP connection establishment, or does it also timeout request.close() and response body reads?

💡 Result:

Dart's HttpClient.connectionTimeout covers only the TCP connection establishment phase (specifically, when connecting to a new host). If the timeout is exceeded, a SocketException is thrown. It does not timeout request.close or response body reads. For timeouts on other phases: - idleTimeout applies to non-active persistent (keep-alive) connections (default 15 seconds). - Timeouts for sending the request (request.close), receiving headers, or reading the response body must be implemented manually using Future.timeout on those operations or by calling request.abort on timeout. There are no built-in HttpClient properties for those phases.

Citations:


Add an end-to-end timeout for proxied requests.

connectionTimeout only bounds TCP connection establishment; request.close() and body reads can still hang on a stalled proxy/server, which blocks tool HTTP calls and proxy tests indefinitely. Manual timeout handling via .timeout() is required for these operations.

🛡️ Proposed fix
 class ExampleHttpService {
   static final ExampleHttpService shared = ExampleHttpService._internal();
+  static const Duration _requestTimeout = Duration(seconds: 30);
@@
-      final request = await client.getUrl(uri);
+      final request = await client.getUrl(uri).timeout(_requestTimeout);
       request.headers.set(HttpHeaders.acceptHeader, 'application/json');
 
-      final response = await request.close();
-      final body = await response.transform(utf8.decoder).join();
+      final response = await request.close().timeout(_requestTimeout);
+      final body =
+          await response.transform(utf8.decoder).join().timeout(_requestTimeout);
@@
-      final request = await client.getUrl(uri);
+      final request = await client.getUrl(uri).timeout(_requestTimeout);
       request.headers.set(HttpHeaders.acceptHeader, 'application/json');
-      final response = await request.close();
+      final response = await request.close().timeout(_requestTimeout);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart`
around lines 27 - 55, The proxied request operations in getJson and testProxy
can hang after TCP connect; update both getJson and testProxy (which use
createScopedHttpClient, request.getUrl, request.close, and
response.transform/join) to apply end-to-end timeouts: wrap the request.close()
Future (and in getJson also the body read response.transform(...).join()) with
.timeout(a reasonable Duration) and handle TimeoutException to return a failed
ExampleHttpResponse or false respectively, ensuring client.close(force: true)
still runs in the finally block; use a shared Duration constant for the timeout
to keep behavior consistent across both methods.

Comment on lines +240 to +416
Future<void> _showProxyConfigDialog(ProxyScope scope) async {
final currentSettings = await ProxySettingsService.shared.load(scope);
final hostController = TextEditingController(text: currentSettings.host);
final portController =
TextEditingController(text: currentSettings.port?.toString() ?? '');
final usernameController =
TextEditingController(text: currentSettings.username);
final passwordController =
TextEditingController(text: currentSettings.password);
var proxyEnabled = currentSettings.enabled &&
currentSettings.scheme.isSupportedInExampleApp;
var selectedScheme = currentSettings.scheme.isSupportedInExampleApp
? currentSettings.scheme
: ProxyScheme.http;
var bypassLocal = currentSettings.bypassLocal;
var showPassword = false;

if (!mounted) return;

await showDialog<void>(
context: context,
builder: (dialogContext) => StatefulBuilder(
builder: (dialogContext, setDialogState) => AlertDialog(
title: Text(scope.displayName),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SwitchListTile(
contentPadding: EdgeInsets.zero,
title: const Text('Enable Proxy'),
value: proxyEnabled,
onChanged: (value) {
setDialogState(() {
proxyEnabled = value;
});
},
),
const SizedBox(height: AppSpacing.smallMedium),
DropdownButtonFormField<ProxyScheme>(
value: selectedScheme,
decoration: const InputDecoration(
labelText: 'Protocol',
border: OutlineInputBorder(),
),
items: ProxyScheme.supportedValues
.map(
(scheme) => DropdownMenuItem(
value: scheme,
child: Text(scheme.displayName),
),
)
.toList(),
onChanged: (value) {
if (value != null) {
setDialogState(() {
selectedScheme = value;
});
}
},
),
const SizedBox(height: AppSpacing.mediumLarge),
TextField(
controller: hostController,
decoration: const InputDecoration(
labelText: 'Host',
hintText: '127.0.0.1',
border: OutlineInputBorder(),
),
),
const SizedBox(height: AppSpacing.mediumLarge),
TextField(
controller: portController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
labelText: 'Port',
hintText: '7890',
border: OutlineInputBorder(),
),
),
const SizedBox(height: AppSpacing.mediumLarge),
TextField(
controller: usernameController,
decoration: const InputDecoration(
labelText: 'Username',
border: OutlineInputBorder(),
),
),
const SizedBox(height: AppSpacing.mediumLarge),
TextField(
controller: passwordController,
obscureText: !showPassword,
decoration: InputDecoration(
labelText: 'Password',
border: const OutlineInputBorder(),
suffixIcon: IconButton(
icon: Icon(
showPassword ? Icons.visibility_off : Icons.visibility,
),
onPressed: () {
setDialogState(() {
showPassword = !showPassword;
});
},
),
),
),
const SizedBox(height: AppSpacing.mediumLarge),
SwitchListTile(
contentPadding: EdgeInsets.zero,
title: const Text('Bypass localhost'),
subtitle: const Text(
'Skip proxy for localhost, 127.0.0.1, and ::1'),
value: bypassLocal,
onChanged: (value) {
setDialogState(() {
bypassLocal = value;
});
},
),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(dialogContext),
child: const Text('Cancel'),
),
TextButton(
onPressed: () async {
final settings = ProxySettings(
enabled: proxyEnabled,
scheme: selectedScheme,
host: hostController.text.trim(),
port: int.tryParse(portController.text.trim()),
username: usernameController.text.trim(),
password: passwordController.text,
bypassLocal: bypassLocal,
);

final result =
await ProxySettingsService.shared.save(scope, settings);
if (!result.isValid) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
result.message ?? 'Invalid proxy settings',
),
),
);
}
return;
}

await _loadProxyConfiguration(scope);

if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('${scope.displayName} settings saved'),
),
);
}

if (dialogContext.mounted) {
Navigator.pop(dialogContext);
}
},
child: const Text('Save'),
),
],
),
),
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect proxy dialog controller creation/disposal.
rg -n -C3 'TextEditingController\(text: currentSettings|hostController\.dispose|portController\.dispose|usernameController\.dispose|passwordController\.dispose' examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 813


Dispose TextEditingControllers after the dialog closes.

The four TextEditingController instances (hostController, portController, usernameController, passwordController) created in this method are never disposed. Repeated dialog opens leak these controller resources. Wrap the showDialog call in a try-finally block to ensure disposal.

♻️ Proposed fix
-    await showDialog<void>(
-      context: context,
-      builder: (dialogContext) => StatefulBuilder(
+    try {
+      await showDialog<void>(
+        context: context,
+        builder: (dialogContext) => StatefulBuilder(
           builder: (dialogContext, setDialogState) => AlertDialog(
             title: Text(scope.displayName),
             content: SingleChildScrollView(
@@
-      ),
-    );
+        ),
+      );
+    } finally {
+      hostController.dispose();
+      portController.dispose();
+      usernameController.dispose();
+      passwordController.dispose();
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Future<void> _showProxyConfigDialog(ProxyScope scope) async {
final currentSettings = await ProxySettingsService.shared.load(scope);
final hostController = TextEditingController(text: currentSettings.host);
final portController =
TextEditingController(text: currentSettings.port?.toString() ?? '');
final usernameController =
TextEditingController(text: currentSettings.username);
final passwordController =
TextEditingController(text: currentSettings.password);
var proxyEnabled = currentSettings.enabled &&
currentSettings.scheme.isSupportedInExampleApp;
var selectedScheme = currentSettings.scheme.isSupportedInExampleApp
? currentSettings.scheme
: ProxyScheme.http;
var bypassLocal = currentSettings.bypassLocal;
var showPassword = false;
if (!mounted) return;
await showDialog<void>(
context: context,
builder: (dialogContext) => StatefulBuilder(
builder: (dialogContext, setDialogState) => AlertDialog(
title: Text(scope.displayName),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SwitchListTile(
contentPadding: EdgeInsets.zero,
title: const Text('Enable Proxy'),
value: proxyEnabled,
onChanged: (value) {
setDialogState(() {
proxyEnabled = value;
});
},
),
const SizedBox(height: AppSpacing.smallMedium),
DropdownButtonFormField<ProxyScheme>(
value: selectedScheme,
decoration: const InputDecoration(
labelText: 'Protocol',
border: OutlineInputBorder(),
),
items: ProxyScheme.supportedValues
.map(
(scheme) => DropdownMenuItem(
value: scheme,
child: Text(scheme.displayName),
),
)
.toList(),
onChanged: (value) {
if (value != null) {
setDialogState(() {
selectedScheme = value;
});
}
},
),
const SizedBox(height: AppSpacing.mediumLarge),
TextField(
controller: hostController,
decoration: const InputDecoration(
labelText: 'Host',
hintText: '127.0.0.1',
border: OutlineInputBorder(),
),
),
const SizedBox(height: AppSpacing.mediumLarge),
TextField(
controller: portController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
labelText: 'Port',
hintText: '7890',
border: OutlineInputBorder(),
),
),
const SizedBox(height: AppSpacing.mediumLarge),
TextField(
controller: usernameController,
decoration: const InputDecoration(
labelText: 'Username',
border: OutlineInputBorder(),
),
),
const SizedBox(height: AppSpacing.mediumLarge),
TextField(
controller: passwordController,
obscureText: !showPassword,
decoration: InputDecoration(
labelText: 'Password',
border: const OutlineInputBorder(),
suffixIcon: IconButton(
icon: Icon(
showPassword ? Icons.visibility_off : Icons.visibility,
),
onPressed: () {
setDialogState(() {
showPassword = !showPassword;
});
},
),
),
),
const SizedBox(height: AppSpacing.mediumLarge),
SwitchListTile(
contentPadding: EdgeInsets.zero,
title: const Text('Bypass localhost'),
subtitle: const Text(
'Skip proxy for localhost, 127.0.0.1, and ::1'),
value: bypassLocal,
onChanged: (value) {
setDialogState(() {
bypassLocal = value;
});
},
),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(dialogContext),
child: const Text('Cancel'),
),
TextButton(
onPressed: () async {
final settings = ProxySettings(
enabled: proxyEnabled,
scheme: selectedScheme,
host: hostController.text.trim(),
port: int.tryParse(portController.text.trim()),
username: usernameController.text.trim(),
password: passwordController.text,
bypassLocal: bypassLocal,
);
final result =
await ProxySettingsService.shared.save(scope, settings);
if (!result.isValid) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
result.message ?? 'Invalid proxy settings',
),
),
);
}
return;
}
await _loadProxyConfiguration(scope);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('${scope.displayName} settings saved'),
),
);
}
if (dialogContext.mounted) {
Navigator.pop(dialogContext);
}
},
child: const Text('Save'),
),
],
),
),
);
}
Future<void> _showProxyConfigDialog(ProxyScope scope) async {
final currentSettings = await ProxySettingsService.shared.load(scope);
final hostController = TextEditingController(text: currentSettings.host);
final portController =
TextEditingController(text: currentSettings.port?.toString() ?? '');
final usernameController =
TextEditingController(text: currentSettings.username);
final passwordController =
TextEditingController(text: currentSettings.password);
var proxyEnabled = currentSettings.enabled &&
currentSettings.scheme.isSupportedInExampleApp;
var selectedScheme = currentSettings.scheme.isSupportedInExampleApp
? currentSettings.scheme
: ProxyScheme.http;
var bypassLocal = currentSettings.bypassLocal;
var showPassword = false;
if (!mounted) return;
try {
await showDialog<void>(
context: context,
builder: (dialogContext) => StatefulBuilder(
builder: (dialogContext, setDialogState) => AlertDialog(
title: Text(scope.displayName),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SwitchListTile(
contentPadding: EdgeInsets.zero,
title: const Text('Enable Proxy'),
value: proxyEnabled,
onChanged: (value) {
setDialogState(() {
proxyEnabled = value;
});
},
),
const SizedBox(height: AppSpacing.smallMedium),
DropdownButtonFormField<ProxyScheme>(
value: selectedScheme,
decoration: const InputDecoration(
labelText: 'Protocol',
border: OutlineInputBorder(),
),
items: ProxyScheme.supportedValues
.map(
(scheme) => DropdownMenuItem(
value: scheme,
child: Text(scheme.displayName),
),
)
.toList(),
onChanged: (value) {
if (value != null) {
setDialogState(() {
selectedScheme = value;
});
}
},
),
const SizedBox(height: AppSpacing.mediumLarge),
TextField(
controller: hostController,
decoration: const InputDecoration(
labelText: 'Host',
hintText: '127.0.0.1',
border: OutlineInputBorder(),
),
),
const SizedBox(height: AppSpacing.mediumLarge),
TextField(
controller: portController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
labelText: 'Port',
hintText: '7890',
border: OutlineInputBorder(),
),
),
const SizedBox(height: AppSpacing.mediumLarge),
TextField(
controller: usernameController,
decoration: const InputDecoration(
labelText: 'Username',
border: OutlineInputBorder(),
),
),
const SizedBox(height: AppSpacing.mediumLarge),
TextField(
controller: passwordController,
obscureText: !showPassword,
decoration: InputDecoration(
labelText: 'Password',
border: const OutlineInputBorder(),
suffixIcon: IconButton(
icon: Icon(
showPassword ? Icons.visibility_off : Icons.visibility,
),
onPressed: () {
setDialogState(() {
showPassword = !showPassword;
});
},
),
),
),
const SizedBox(height: AppSpacing.mediumLarge),
SwitchListTile(
contentPadding: EdgeInsets.zero,
title: const Text('Bypass localhost'),
subtitle: const Text(
'Skip proxy for localhost, 127.0.0.1, and ::1'),
value: bypassLocal,
onChanged: (value) {
setDialogState(() {
bypassLocal = value;
});
},
),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(dialogContext),
child: const Text('Cancel'),
),
TextButton(
onPressed: () async {
final settings = ProxySettings(
enabled: proxyEnabled,
scheme: selectedScheme,
host: hostController.text.trim(),
port: int.tryParse(portController.text.trim()),
username: usernameController.text.trim(),
password: passwordController.text,
bypassLocal: bypassLocal,
);
final result =
await ProxySettingsService.shared.save(scope, settings);
if (!result.isValid) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
result.message ?? 'Invalid proxy settings',
),
),
);
}
return;
}
await _loadProxyConfiguration(scope);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('${scope.displayName} settings saved'),
),
);
}
if (dialogContext.mounted) {
Navigator.pop(dialogContext);
}
},
child: const Text('Save'),
),
],
),
),
);
} finally {
hostController.dispose();
portController.dispose();
usernameController.dispose();
passwordController.dispose();
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart`
around lines 240 - 416, The TextEditingController instances created in
_showProxyConfigDialog (hostController, portController, usernameController,
passwordController) are not disposed and leak; wrap the await showDialog(...)
call in a try/finally inside _showProxyConfigDialog so that in the finally block
you call dispose() on each controller (hostController.dispose(),
portController.dispose(), usernameController.dispose(),
passwordController.dispose()) to ensure they are always cleaned up after the
dialog closes or an error occurs.

Comment on lines +1125 to +1133
OutlinedButton(
onPressed: () {
unawaited(_testProxyConfiguration(scope));
},
style: OutlinedButton.styleFrom(
foregroundColor: AppColors.primaryGreen,
),
child: const Text('Test'),
),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don’t report a proxy test success when the proxy is disabled.

The Test button is always enabled; if the scope is disabled or incomplete, ExampleHttpService.createScopedHttpClient uses DIRECT, so a successful request can be shown as “proxy test succeeded” even though no proxy was used.

🐛 Proposed fix
                 OutlinedButton(
-                  onPressed: () {
-                    unawaited(_testProxyConfiguration(scope));
-                  },
+                  onPressed: isConfigured
+                      ? () {
+                          unawaited(_testProxyConfiguration(scope));
+                        }
+                      : null,
                   style: OutlinedButton.styleFrom(
                     foregroundColor: AppColors.primaryGreen,
                   ),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart`
around lines 1125 - 1133, The Test button currently always calls
_testProxyConfiguration(scope) which can show "proxy test succeeded" even when
the proxy scope is disabled or incomplete because
ExampleHttpService.createScopedHttpClient falls back to DIRECT; update the UI
and handler so the button is disabled (onPressed == null) when the provided
scope is disabled or missing required fields (e.g., check scope.enabled or
scope.isEnabled and required host/port/auth fields), and/or make
_testProxyConfiguration(scope) return early with an appropriate message if
scope.enabled is false or validation fails; locate the button widget and the
_testProxyConfiguration function and add the guard/validation to prevent running
the test when the proxy is not actually configured.

Comment on lines +35 to +37
flutter_secure_storage: ^9.0.0
flutter_secure_storage_windows:
path: vendor/flutter_secure_storage_windows
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect pinned versions and platform-interface constraints
fd -t f 'pubspec.yaml' examples/flutter/RunAnywhereAI | head -20
cat examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/pubspec.yaml
fd -t f 'pubspec.lock' examples/flutter/RunAnywhereAI 2>/dev/null | xargs -I {} sh -c 'echo "=== {} ==="; grep -A2 -E "flutter_secure_storage" {}'

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 955


🌐 Web query:

flutter_secure_storage 9.0.0 required flutter_secure_storage_windows version

💡 Result:

flutter_secure_storage 9.0.0 requires flutter_secure_storage_windows ^3.0.0 or compatible version that supports FFI with win32 package. The main package depends on the platform-specific flutter_secure_storage_windows package, which was updated in 9.0.0 to migrate Windows implementation to FFI using win32 (from changelog). Do not add flutter_secure_storage_windows directly; use the main flutter_secure_storage: ^9.0.0 in pubspec.yaml, and run flutter pub get to resolve transitive dependencies automatically. The package supports Windows as confirmed by dependencies listed on pub.dev (flutter, flutter_secure_storage_windows, etc.). Latest current version is 10.0.0, but for exactly 9.0.0, the Windows impl version is the one bundled/released with it, likely around 3.x based on similar forks like flutter_secure_storage_x_windows ^3.4.1.

Citations:


🏁 Script executed:

#!/bin/bash
# Check the main pubspec.yaml for dependency_overrides and full dependency tree
head -50 examples/flutter/RunAnywhereAI/pubspec.yaml
tail -20 examples/flutter/RunAnywhereAI/pubspec.yaml

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 2069


🏁 Script executed:

#!/bin/bash
# Check if pubspec.lock exists and inspect platform_interface resolution
if [ -f "examples/flutter/RunAnywhereAI/pubspec.lock" ]; then
  echo "=== Checking pubspec.lock ==="
  grep -A5 "flutter_secure_storage_platform_interface" examples/flutter/RunAnywhereAI/pubspec.lock | head -30
  echo ""
  echo "=== All flutter_secure_storage entries ==="
  grep -B2 -A3 "flutter_secure_storage" examples/flutter/RunAnywhereAI/pubspec.lock | head -60
else
  echo "No pubspec.lock found"
fi

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 95


🌐 Web query:

flutter_secure_storage_platform_interface 1.1.1 flutter_secure_storage 9.0.0 compatibility

💡 Result:

No, flutter_secure_storage 9.0.0 is not compatible with flutter_secure_storage_platform_interface 1.1.1. The platform interface version 1.1.1 was released about 22 months ago (around mid-2024), while flutter_secure_storage 9.0.0 includes breaking changes like Windows FFI migration (from changelog in 9.2.4 referencing 9.0.0) and depends on a newer version of the platform interface, likely ^2.0.0 or higher as seen in current develop branch (^2.0.1). Newer platform interface versions (2.0+) require Dart SDK 3.3+, while 1.1.1 supports Dart 2.12+, indicating API changes that would cause incompatibility. Pub dependency resolution would fail if constraints do not overlap.

Citations:


Incompatible version constraints between flutter_secure_storage: ^9.0.0 and the vendored flutter_secure_storage_windows 3.1.2.

The vendored flutter_secure_storage_windows (version 3.1.2) depends on flutter_secure_storage_platform_interface: ^1.1.1, but flutter_secure_storage: ^9.0.0 requires flutter_secure_storage_platform_interface: ^2.0.0 or higher. This constraint mismatch will cause flutter pub get to fail with a dependency resolution error on Windows.

To resolve:

  1. Upgrade the vendored flutter_secure_storage_windows to version 4.x or higher (which supports platform_interface ^2.0.0+), or
  2. Downgrade flutter_secure_storage to a version compatible with platform_interface ^1.1.1 (e.g., ^8.x), or
  3. Remove the vendored override and rely on the automatic transitive dependency resolution via flutter_secure_storage: ^9.0.0.

Also applies to: 76-77

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/pubspec.yaml` around lines 35 - 37, The
pubspec lists flutter_secure_storage: ^9.0.0 but uses a vendored
flutter_secure_storage_windows (v3.1.2) that requires
flutter_secure_storage_platform_interface ^1.1.1, causing a platform_interface
version conflict; fix by either (preferred) updating the vendored
flutter_secure_storage_windows to a 4.x+ release that supports
flutter_secure_storage_platform_interface ^2.0.0 (replace the vendor folder or
point the dependency to a compatible 4.x source), or remove the vendored
flutter_secure_storage_windows override so pub can resolve the transitive
4.x-compatible Windows package, or alternatively downgrade
flutter_secure_storage to a 8.x release that matches platform_interface
^1.1.1—apply the chosen change in pubspec.yaml and run flutter pub get to verify
resolution.

Comment on lines +147 to +149
UpdateTheme(window);

return OnCreate();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Tear down the HWND when OnCreate() fails.

Line 149 returns false without destroying the native window created at Line 137, leaving callers with a failed Create() but a live HWND until some later cleanup path runs.

🐛 Proposed fix
   UpdateTheme(window);
 
-  return OnCreate();
+  if (!OnCreate()) {
+    Destroy();
+    return false;
+  }
+
+  return true;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp` around lines
147 - 149, OnCreate() can fail but the code currently returns false without
destroying the HWND created earlier; update the Create() flow so that after
calling UpdateTheme(window) you call OnCreate() and if it returns false you call
::DestroyWindow(window) (and clear the stored HWND/member window handle) before
returning false, ensuring the native window is torn down when OnCreate() fails.

Comment on lines +181 to +221
switch (message) {
case WM_DESTROY:
window_handle_ = nullptr;
OnDestroy();
if (quit_on_close_) {
PostQuitMessage(0);
}
return 0;

case WM_DPICHANGED: {
auto newRectSize = reinterpret_cast<RECT*>(lparam);
LONG newWidth = newRectSize->right - newRectSize->left;
LONG newHeight = newRectSize->bottom - newRectSize->top;

SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
newHeight, SWP_NOZORDER | SWP_NOACTIVATE);

return 0;
}
case WM_SIZE: {
RECT rect = GetClientArea();
if (child_content_ != nullptr) {
// Size and position the child window.
MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
rect.bottom - rect.top, TRUE);
}
return 0;
}

case WM_ACTIVATE:
if (child_content_ != nullptr) {
SetFocus(child_content_);
}
return 0;

case WM_DWMCOLORIZATIONCOLORCHANGED:
UpdateTheme(hwnd);
return 0;
}

return DefWindowProc(window_handle_, message, wparam, lparam);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Confirm whether default message handling still uses the member
# HWND after WM_DESTROY clears it.

set -euo pipefail

file="$(fd -i '^win32_window\.cpp$' | head -n 1)"
test -n "$file"

rg -n -C5 'case WM_DESTROY|case WM_NCDESTROY|window_handle_ = nullptr|DefWindowProc\((window_handle_|hwnd)' "$file"

# Expected after the fix:
# - No fallback call to DefWindowProc(window_handle_, ...).
# - A WM_NCDESTROY path clears GWLP_USERDATA and calls DefWindowProc(hwnd, ...).

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 969


Pass the HWND parameter instead of the cleared member variable to DefWindowProc, and add WM_NCDESTROY handling.

After WM_DESTROY clears window_handle_, subsequent messages like WM_NCDESTROY fall through to the default handler at line 221. Passing nullptr to DefWindowProc violates the Windows API contract and skips required cleanup. Use the hwnd parameter and add a WM_NCDESTROY handler to clear GWLP_USERDATA:

Proposed fix
    case WM_DWMCOLORIZATIONCOLORCHANGED:
      UpdateTheme(hwnd);
      return 0;
+
+    case WM_NCDESTROY:
+      SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
+      return DefWindowProc(hwnd, message, wparam, lparam);
  }
 
-  return DefWindowProc(window_handle_, message, wparam, lparam);
+  return DefWindowProc(hwnd, message, wparam, lparam);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp` around lines
181 - 221, The switch falls through to DefWindowProc using the cleared member
window_handle_; change the final call to use the incoming hwnd parameter (call
DefWindowProc(hwnd, message, wparam, lparam)) and add a WM_NCDESTROY case that
clears the per-window user data (SetWindowLongPtr(hwnd, GWLP_USERDATA, 0)) and
performs any final cleanup after WM_DESTROY (e.g., ensure window_handle_ is
already nulled and return 0), so default processing gets the correct hwnd and
GWLP_USERDATA is cleared when the non-client destroy message arrives.

Comment on lines +224 to +231
void Win32Window::Destroy() {
if (window_handle_) {
DestroyWindow(window_handle_);
window_handle_ = nullptr;
}
if (g_active_window_count == 0) {
WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Confirm DestroyWindow result handling in Win32Window::Destroy.

set -euo pipefail

file="$(fd -i '^win32_window\.cpp$' | head -n 1)"
test -n "$file"

rg -n -C6 'void Win32Window::Destroy|DestroyWindow\(|window_handle_ = nullptr|UnregisterWindowClass' "$file"

# Expected after the fix:
# - DestroyWindow(...) result is checked.
# - window_handle_ is not cleared on the failure path.
# - class unregistration is skipped when destruction failed.

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 1835


Ignore DestroyWindow failure result, causing improper resource cleanup.

DestroyWindow returns a BOOL indicating success, but line 228 ignores this result. Line 229 unconditionally clears window_handle_, and line 232 may unregister the window class even if the window destruction failed. Preserve state and skip class unregistration if DestroyWindow fails.

Proposed fix
 void Win32Window::Destroy() {
   OnDestroy();

   if (window_handle_) {
-    DestroyWindow(window_handle_);
-    window_handle_ = nullptr;
+    HWND window = window_handle_;
+    if (!DestroyWindow(window)) {
+      return;
+    }
+    if (window_handle_ == window) {
+      window_handle_ = nullptr;
+    }
   }
   if (g_active_window_count == 0) {
     WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp` around lines
224 - 231, The Destroy method currently ignores DestroyWindow's BOOL result and
unconditionally clears window_handle_ and potentially unregisters the class;
update Win32Window::Destroy to capture the return value of
DestroyWindow(window_handle_), only set window_handle_ to nullptr if
DestroyWindow succeeds, and only decrement/consider g_active_window_count and
call WindowClassRegistrar::GetInstance()->UnregisterWindowClass() when
destruction succeeded; if DestroyWindow fails, preserve window_handle_ (so
callers can retry or cleanup later) and surface/log the failure (e.g., via
GetLastError) rather than proceeding to unregister the class.

Comment on lines +8 to +16
if "%~1"=="" goto args_done
if /I "%~1"=="all" set "BACKENDS=all"
if /I "%~1"=="llamacpp" set "BACKENDS=llamacpp"
if /I "%~1"=="onnx" set "BACKENDS=onnx"
if /I "%~1"=="--clean" set "CLEAN=1"
if /I "%~1"=="-h" goto usage
if /I "%~1"=="--help" goto usage
shift
goto :parse_args

:done_args
goto parse_args
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Reject unknown build arguments instead of ignoring them.

A typo or stale option like --shared is currently ignored and the script continues with the default backend, which can produce the wrong artifact set while still succeeding.

Proposed argument validation
 :parse_args
 if "%~1"=="" goto args_done
-if /I "%~1"=="all" set "BACKENDS=all"
-if /I "%~1"=="llamacpp" set "BACKENDS=llamacpp"
-if /I "%~1"=="onnx" set "BACKENDS=onnx"
-if /I "%~1"=="--clean" set "CLEAN=1"
+if /I "%~1"=="all" (
+  set "BACKENDS=all"
+  goto arg_ok
+)
+if /I "%~1"=="llamacpp" (
+  set "BACKENDS=llamacpp"
+  goto arg_ok
+)
+if /I "%~1"=="onnx" (
+  set "BACKENDS=onnx"
+  goto arg_ok
+)
+if /I "%~1"=="--clean" (
+  set "CLEAN=1"
+  goto arg_ok
+)
 if /I "%~1"=="-h" goto usage
 if /I "%~1"=="--help" goto usage
+echo ERROR: Unknown argument: %~1
+echo Usage: build-windows.bat [all^|llamacpp^|onnx] [--clean]
+exit /b 1
+:arg_ok
 shift
 goto parse_args
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-commons/scripts/build-windows.bat` around lines 8 - 16, The
argument parsing currently silently ignores unknown options, so update the parse
logic around the if "%~1" checks (the parse_args loop / parse_args label) to
reject unrecognized arguments: when none of the known branches (all, llamacpp,
onnx, --clean, -h, --help) match, print a clear error mentioning the bad token
and call the existing usage flow (goto usage) or exit /b 1; ensure variables
like BACKENDS and CLEAN are only set when recognized and that the parser halts
with non-zero status for unknown options to prevent accidental successful builds
with wrong defaults.

Comment on lines +100 to +106
if /I "%BUILD_ONNX%"=="ON" (
for %%F in ("%ROOT%\third_party\sherpa-onnx-windows\bin\*.dll") do (
if exist "%%~F" copy /y "%%~F" "%DIST_DIR%\" >nul
)
for %%F in ("%ROOT%\third_party\sherpa-onnx-windows\lib\*.dll") do (
if exist "%%~F" copy /y "%%~F" "%DIST_DIR%\" >nul
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fail if Sherpa dependency DLL copies fail.

The ONNX dependency copy commands currently discard failures, so a locked file, permission issue, or full disk can still end with “Build complete” and an incomplete dist.

Proposed copy error handling
 if /I "%BUILD_ONNX%"=="ON" (
   for %%F in ("%ROOT%\third_party\sherpa-onnx-windows\bin\*.dll") do (
-    if exist "%%~F" copy /y "%%~F" "%DIST_DIR%\" >nul
+    if exist "%%~F" (
+      copy /y "%%~F" "%DIST_DIR%\" >nul
+      if errorlevel 1 (
+        echo ERROR: Failed to copy %%~nxF to %DIST_DIR%.
+        exit /b 1
+      )
+    )
   )
   for %%F in ("%ROOT%\third_party\sherpa-onnx-windows\lib\*.dll") do (
-    if exist "%%~F" copy /y "%%~F" "%DIST_DIR%\" >nul
+    if exist "%%~F" (
+      copy /y "%%~F" "%DIST_DIR%\" >nul
+      if errorlevel 1 (
+        echo ERROR: Failed to copy %%~nxF to %DIST_DIR%.
+        exit /b 1
+      )
+    )
   )
 )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if /I "%BUILD_ONNX%"=="ON" (
for %%F in ("%ROOT%\third_party\sherpa-onnx-windows\bin\*.dll") do (
if exist "%%~F" copy /y "%%~F" "%DIST_DIR%\" >nul
)
for %%F in ("%ROOT%\third_party\sherpa-onnx-windows\lib\*.dll") do (
if exist "%%~F" copy /y "%%~F" "%DIST_DIR%\" >nul
)
if /I "%BUILD_ONNX%"=="ON" (
for %%F in ("%ROOT%\third_party\sherpa-onnx-windows\bin\*.dll") do (
if exist "%%~F" (
copy /y "%%~F" "%DIST_DIR%\" >nul
if errorlevel 1 (
echo ERROR: Failed to copy %%~nxF to %DIST_DIR%.
exit /b 1
)
)
)
for %%F in ("%ROOT%\third_party\sherpa-onnx-windows\lib\*.dll") do (
if exist "%%~F" (
copy /y "%%~F" "%DIST_DIR%\" >nul
if errorlevel 1 (
echo ERROR: Failed to copy %%~nxF to %DIST_DIR%.
exit /b 1
)
)
)
)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-commons/scripts/build-windows.bat` around lines 100 - 106,
The current Windows batch loop that copies Sherpa ONNX DLLs (the block gated by
BUILD_ONNX) swallows copy failures by redirecting output to nul; change it so
each copy checks the copy exit status (ERRORLEVEL) after the copy of each file
from "%ROOT%\third_party\sherpa-onnx-windows\bin\*.dll" and
"%ROOT%\third_party\sherpa-onnx-windows\lib\*.dll" and, on failure, emit an
error to the console and exit the script with non‑zero status (e.g., exit /b 1)
so the build fails instead of reporting “Build complete” with missing DLLs.
Ensure you apply this check inside both FOR loops that perform the copy to
"%DIST_DIR%\".

Comment on lines 203 to 215
final totalFiles = multiFile.files.length;
final totalModelBytes = model.downloadSize ?? 0;
var cumulativeDownloaded = 0;
_logger.info('Multi-file model: downloading $totalFiles files');
yield ModelDownloadProgress.started(modelId, model.downloadSize ?? 0);
yield ModelDownloadProgress.started(modelId, totalModelBytes);

for (var i = 0; i < multiFile.files.length; i++) {
final descriptor = multiFile.files[i];
final fileUrl = descriptor.url;
if (fileUrl == null) {
_logger.warning('No URL for file descriptor: ${descriptor.destinationPath}');
_logger.warning(
'No URL for file descriptor: ${descriptor.destinationPath}');
continue;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fail invalid multi-file manifests instead of completing partial downloads.

An empty files list or a descriptor with url == null currently falls through to _updateModelLocalPath(...) and publishes downloadCompleted, registering an incomplete model as usable.

Reject incomplete multi-file manifests
           final totalFiles = multiFile.files.length;
+          if (totalFiles == 0) {
+            throw StateError('Multi-file model has no files: $modelId');
+          }
           final totalModelBytes = model.downloadSize ?? 0;
           var cumulativeDownloaded = 0;
           _logger.info('Multi-file model: downloading $totalFiles files');
           yield ModelDownloadProgress.started(modelId, totalModelBytes);
@@
             final fileUrl = descriptor.url;
             if (fileUrl == null) {
               _logger.warning(
                   'No URL for file descriptor: ${descriptor.destinationPath}');
-              continue;
+              throw StateError(
+                'No URL for file descriptor: ${descriptor.destinationPath}',
+              );
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
final totalFiles = multiFile.files.length;
final totalModelBytes = model.downloadSize ?? 0;
var cumulativeDownloaded = 0;
_logger.info('Multi-file model: downloading $totalFiles files');
yield ModelDownloadProgress.started(modelId, model.downloadSize ?? 0);
yield ModelDownloadProgress.started(modelId, totalModelBytes);
for (var i = 0; i < multiFile.files.length; i++) {
final descriptor = multiFile.files[i];
final fileUrl = descriptor.url;
if (fileUrl == null) {
_logger.warning('No URL for file descriptor: ${descriptor.destinationPath}');
_logger.warning(
'No URL for file descriptor: ${descriptor.destinationPath}');
continue;
final totalFiles = multiFile.files.length;
if (totalFiles == 0) {
throw StateError('Multi-file model has no files: $modelId');
}
final totalModelBytes = model.downloadSize ?? 0;
var cumulativeDownloaded = 0;
_logger.info('Multi-file model: downloading $totalFiles files');
yield ModelDownloadProgress.started(modelId, totalModelBytes);
for (var i = 0; i < multiFile.files.length; i++) {
final descriptor = multiFile.files[i];
final fileUrl = descriptor.url;
if (fileUrl == null) {
_logger.warning(
'No URL for file descriptor: ${descriptor.destinationPath}');
throw StateError(
'No URL for file descriptor: ${descriptor.destinationPath}',
);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart`
around lines 203 - 215, The multi-file download currently treats empty or
partially missing descriptors as success; before calling _updateModelLocalPath
or emitting downloadCompleted, validate the manifest and abort on invalid data:
if multiFile.files.isEmpty, log an error via _logger.error and emit a failure
progress (e.g. ModelDownloadProgress.failed or throw) and return; likewise
inside the loop if descriptor.url == null, immediately log the error, emit a
failure and stop the download rather than continue to the next file. Ensure
neither _updateModelLocalPath nor the final downloadCompleted progress are
called when the manifest is invalid.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Fix All in Cursor

Bugbot Autofix is ON, but it could not run because the branch was deleted or merged before autofix could start.

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit c29f567. Configure here.

ProxyScheme.https =>
'PROXY ${settings.host.trim()}:${settings.port}',
ProxyScheme.socks5 => null,
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

HTTPS proxy scheme treated identically to HTTP

Medium Severity

ProxyScheme.https is listed as supported via isSupportedInExampleApp and shown to users, but _proxyDirectiveForSettings generates the exact same PROXY host:port directive for both HTTP and HTTPS schemes. Dart's findProxy PROXY keyword always uses a plain HTTP connection to the proxy. A user selecting HTTPS may expect the proxy connection itself to be TLS-encrypted, but it behaves identically to HTTP, which is misleading and could create a false sense of security.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit c29f567. Configure here.

Future<bool> testGeneralProxy(Uri uri) async {
final response = await getJson(uri);
return response.statusCode >= 200 && response.statusCode < 300;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unused testGeneralProxy method is dead code

Low Severity

testGeneralProxy is a new public method that is never called anywhere in the codebase. The settings UI uses testProxy (which accepts a ProxyScope parameter) instead. This is dead code that adds unnecessary API surface.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit c29f567. Configure here.

}

_current[scope] = settings;
return settings;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Legacy proxy path reads keychain credentials twice

Low Severity

In load(), credentials are read from the keychain at the top for every call. When the legacy path is taken (scope == ProxyScope.general && !hasScopedPreferences), _loadLegacyGeneralSettings reads the exact same keychain keys again, and the initial reads are silently discarded. This results in redundant async I/O with no functional benefit.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit c29f567. Configure here.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt (2)

284-297: ⚠️ Potential issue | 🔴 Critical

Second duplicate elseif(RAC_PLATFORM_WINDOWS) branch — dead code.

Same problem as the GGML section above: lines 284–288 add a new Windows branch, but the pre-existing Windows branch at lines 294–297 is still present. The chain reaches line 284 first, so lines 294–297 never execute. This is benign here (only a duplicated status message), but it should be removed to avoid confusion and to keep the file consistent with the AI summary.

🛠️ Proposed fix: remove the trailing duplicate
 elseif(RAC_PLATFORM_WINDOWS)
     message(STATUS "Configuring LlamaCPP backend for Windows")
     target_compile_definitions(rac_backend_llamacpp PRIVATE NOMINMAX WIN32_LEAN_AND_MEAN)
     target_link_libraries(rac_backend_llamacpp PUBLIC ws2_32)

 elseif(RAC_PLATFORM_LINUX)
     message(STATUS "Configuring LlamaCPP backend for Linux")
     # Linux-specific link libraries
     target_link_libraries(rac_backend_llamacpp PUBLIC pthread dl)
-
-elseif(RAC_PLATFORM_WINDOWS)
-    message(STATUS "Configuring LlamaCPP backend for Windows")
-    # No extra link libraries needed on Windows (threading is built-in)
 endif()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt` around lines
284 - 297, Remove the duplicate elseif(RAC_PLATFORM_WINDOWS) branch that follows
the existing one: locate the repeated block that sets message(STATUS
"Configuring LlamaCPP backend for Windows") and the Windows-specific
target_compile_definitions/target_link_libraries for rac_backend_llamacpp, and
delete the trailing duplicate so only the first RAC_PLATFORM_WINDOWS branch
remains; ensure the remaining branch retains
target_compile_definitions(rac_backend_llamacpp PRIVATE NOMINMAX
WIN32_LEAN_AND_MEAN) and target_link_libraries(rac_backend_llamacpp PUBLIC
ws2_32) and no other changes to the RAC_PLATFORM_LINUX branch.

76-113: ⚠️ Potential issue | 🔴 Critical

Duplicate elseif(RAC_PLATFORM_WINDOWS) branch — second branch is dead code and loses GGML_OPENMP OFF.

Two elseif(RAC_PLATFORM_WINDOWS) branches now exist in the same if/elseif chain (lines 76–85 and 100–113). CMake only evaluates the first matching branch, so the second one is unreachable. In particular, set(GGML_OPENMP OFF ...) from the old block (line 110) is silently dropped on Windows, along with the status message. The AI summary describes removing the duplicate block, but it is still present in the committed code.

🛠️ Proposed fix: consolidate into a single Windows branch
 elseif(RAC_PLATFORM_WINDOWS)
     set(GGML_METAL OFF CACHE BOOL "" FORCE)
     set(GGML_VULKAN OFF CACHE BOOL "" FORCE)
     set(GGML_CUDA OFF CACHE BOOL "" FORCE)
     set(GGML_OPENCL OFF CACHE BOOL "" FORCE)
     set(GGML_HIPBLAS OFF CACHE BOOL "" FORCE)
     set(GGML_SYCL OFF CACHE BOOL "" FORCE)
     set(GGML_KOMPUTE OFF CACHE BOOL "" FORCE)
     set(GGML_RPC OFF CACHE BOOL "" FORCE)
     set(GGML_NATIVE OFF CACHE BOOL "" FORCE)
+    set(GGML_OPENMP OFF CACHE BOOL "" FORCE)
+    message(STATUS "Configuring llama.cpp for Windows (CPU-only)")
 elseif(RAC_PLATFORM_LINUX)
     # Disable GPU backends not typically available on Linux ARM
     set(GGML_METAL OFF CACHE BOOL "" FORCE)
     set(GGML_CUDA OFF CACHE BOOL "" FORCE)
     set(GGML_VULKAN OFF CACHE BOOL "" FORCE)
     set(GGML_OPENCL OFF CACHE BOOL "" FORCE)
     set(GGML_HIPBLAS OFF CACHE BOOL "" FORCE)
     set(GGML_SYCL OFF CACHE BOOL "" FORCE)

     # Enable ARM NEON for aarch64 (Raspberry Pi 5)
     if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
         set(GGML_NEON ON CACHE BOOL "" FORCE)
         message(STATUS "Enabling NEON for Linux aarch64")
     endif()
-elseif(RAC_PLATFORM_WINDOWS)
-    # Windows: CPU-only by default, no Metal/CUDA/Vulkan
-    set(GGML_METAL OFF CACHE BOOL "" FORCE)
-    set(GGML_CUDA OFF CACHE BOOL "" FORCE)
-    set(GGML_VULKAN OFF CACHE BOOL "" FORCE)
-    set(GGML_OPENCL OFF CACHE BOOL "" FORCE)
-    set(GGML_HIPBLAS OFF CACHE BOOL "" FORCE)
-    set(GGML_SYCL OFF CACHE BOOL "" FORCE)
-    set(GGML_KOMPUTE OFF CACHE BOOL "" FORCE)
-    set(GGML_RPC OFF CACHE BOOL "" FORCE)
-    set(GGML_OPENMP OFF CACHE BOOL "" FORCE)
-    set(GGML_NATIVE OFF CACHE BOOL "" FORCE)
-    message(STATUS "Configuring llama.cpp for Windows (CPU-only)")
 endif()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt` around lines 76
- 113, There are two elseif(RAC_PLATFORM_WINDOWS) branches; remove the duplicate
earlier branch and consolidate into a single Windows branch that sets
GGML_METAL, GGML_VULKAN, GGML_CUDA, GGML_OPENCL, GGML_HIPBLAS, GGML_SYCL,
GGML_KOMPUTE, GGML_RPC, GGML_NATIVE OFF (CACHE BOOL "" FORCE) and also set
GGML_OPENMP OFF (CACHE BOOL "" FORCE) and emit the message(STATUS "Configuring
llama.cpp for Windows (CPU-only)"); update or delete the first occurrence so the
final if/elseif chain contains only one RAC_PLATFORM_WINDOWS handling and
preserves GGML_OPENMP OFF and the status message.
sdk/runanywhere-commons/scripts/windows/download-sherpa-onnx.bat (1)

66-71: ⚠️ Potential issue | 🟡 Minor

Add --fail (or -f) to curl to reject HTTP error responses.

Without --fail, curl treats a 4xx/5xx response as success and writes the error body to sherpa-onnx.tar.bz2. The errorlevel check on Line 67 then won't trigger; the failure only surfaces later as a misleading "Extraction failed." --fail also interacts correctly with --retry, so transient 5xx responses will actually be retried.

Proposed fix
-curl -L --retry 5 --retry-delay 2 --retry-all-errors -o "%TEMP_DL%\sherpa-onnx.tar.bz2" "%URL%"
+curl -fL --retry 5 --retry-delay 2 --retry-all-errors -o "%TEMP_DL%\sherpa-onnx.tar.bz2" "%URL%"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-commons/scripts/windows/download-sherpa-onnx.bat` around
lines 66 - 71, The curl invocation that downloads
"%TEMP_DL%\sherpa-onnx.tar.bz2" should include the --fail (or -f) flag so HTTP
4xx/5xx responses cause curl to exit non‑zero and trigger the existing
errorlevel branch; update the curl command (the line starting with curl -L
--retry 5 ...) to add --fail (or -f) alongside the existing flags so transient
5xx responses are still retried and plain error pages are not written to the
tarball.
♻️ Duplicate comments (9)
sdk/runanywhere-commons/scripts/build-windows.bat (2)

100-107: ⚠️ Potential issue | 🟠 Major

Sherpa DLL copy failures are still swallowed.

The if exist ... copy /y ... >nul pattern discards the copy exit code, so a locked file, AV hold, or permission error silently completes with "Build complete" and an incomplete dist. Please check errorlevel after each copy and abort, as suggested previously.

Proposed fix
 if /I "%BUILD_ONNX%"=="ON" (
   for %%F in ("%ROOT%\third_party\sherpa-onnx-windows\bin\*.dll") do (
-    if exist "%%~F" copy /y "%%~F" "%DIST_DIR%\" >nul
+    if exist "%%~F" (
+      copy /y "%%~F" "%DIST_DIR%\" >nul
+      if errorlevel 1 (
+        echo ERROR: Failed to copy %%~nxF to %DIST_DIR%.
+        exit /b 1
+      )
+    )
   )
   for %%F in ("%ROOT%\third_party\sherpa-onnx-windows\lib\*.dll") do (
-    if exist "%%~F" copy /y "%%~F" "%DIST_DIR%\" >nul
+    if exist "%%~F" (
+      copy /y "%%~F" "%DIST_DIR%\" >nul
+      if errorlevel 1 (
+        echo ERROR: Failed to copy %%~nxF to %DIST_DIR%.
+        exit /b 1
+      )
+    )
   )
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-commons/scripts/build-windows.bat` around lines 100 - 107,
The for-loop copy blocks guarded by the BUILD_ONNX check (the two for %%F in
("%ROOT%\third_party\sherpa-onnx-windows\bin\*.dll") and for %%F in
("%ROOT%\third_party\sherpa-onnx-windows\lib\*.dll") that copy to "%DIST_DIR%\")
currently redirect output to >nul and therefore swallow failures; after each
copy command check the ERRORLEVEL (or use conditional &&/||) and if the copy
failed, print an error via echo and exit /b with a non-zero code so the build
aborts and the failure is visible.

7-16: ⚠️ Potential issue | 🟡 Minor

Unknown arguments are still silently ignored.

A typo like build-windows.bat onnxx falls through parse_args leaving BACKENDS=llamacpp and produces a silently wrong artifact set. Please reject unrecognized tokens with a non-zero exit and a usage message, as previously suggested.

Proposed fix
 :parse_args
 if "%~1"=="" goto args_done
-if /I "%~1"=="all" set "BACKENDS=all"
-if /I "%~1"=="llamacpp" set "BACKENDS=llamacpp"
-if /I "%~1"=="onnx" set "BACKENDS=onnx"
-if /I "%~1"=="--clean" set "CLEAN=1"
-if /I "%~1"=="-h" goto usage
-if /I "%~1"=="--help" goto usage
+if /I "%~1"=="all" ( set "BACKENDS=all" & goto arg_ok )
+if /I "%~1"=="llamacpp" ( set "BACKENDS=llamacpp" & goto arg_ok )
+if /I "%~1"=="onnx" ( set "BACKENDS=onnx" & goto arg_ok )
+if /I "%~1"=="--clean" ( set "CLEAN=1" & goto arg_ok )
+if /I "%~1"=="-h" goto usage
+if /I "%~1"=="--help" goto usage
+echo ERROR: Unknown argument: %~1
+goto usage
+:arg_ok
 shift
 goto parse_args
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-commons/scripts/build-windows.bat` around lines 7 - 16, The
parse_args routine (label :parse_args) currently ignores unknown tokens and can
leave BACKENDS set incorrectly; update it so that after the recognized checks
for "all", "llamacpp", "onnx", "--clean", "-h", and "--help" any unrecognized
argument triggers printing the usage (reuse the existing usage handling) and
exits non-zero (set ERROR or call exit /b 1) instead of silently continuing;
ensure the unknown-arg branch runs before the shift/goto parse_args loop so
tokens like "onnxx" cause an immediate usage message and non-zero exit rather
than falling through and leaving BACKENDS incorrectly.
examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart (2)

1125-1133: ⚠️ Potential issue | 🟠 Major

Test button can report "success" when the proxy is disabled/incomplete.

The Test button is always enabled. When isConfigured is false, _testProxyConfiguration still runs, and ExampleHttpService.createScopedHttpClient falls back to DIRECT — so the snackbar reports "${scope.displayName} test succeeded" even though no proxy was actually exercised. Gate the onPressed on isConfigured (or early-return inside _testProxyConfiguration with an explicit message).

🐛 Proposed fix
                 OutlinedButton(
-                  onPressed: () {
-                    unawaited(_testProxyConfiguration(scope));
-                  },
+                  onPressed: isConfigured
+                      ? () {
+                          unawaited(_testProxyConfiguration(scope));
+                        }
+                      : null,
                   style: OutlinedButton.styleFrom(
                     foregroundColor: AppColors.primaryGreen,
                   ),
                   child: const Text('Test'),
                 ),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart`
around lines 1125 - 1133, The Test button is always enabled so
_testProxyConfiguration runs even when a proxy is not configured and
ExampleHttpService.createScopedHttpClient falls back to DIRECT, causing false
"test succeeded" snackbars; fix by gating the OutlinedButton's onPressed on the
setting that indicates a configured proxy (isConfigured) or add an early return
at the start of _testProxyConfiguration that checks isConfigured and shows an
explicit snackbar/error instead of proceeding; update the
OutlinedButton(onPressed: ...) to only call
unawaited(_testProxyConfiguration(scope)) when isConfigured is true (or have
_testProxyConfiguration(scope) check isConfigured and return with a clear
message using scope.displayName).

240-416: ⚠️ Potential issue | 🟡 Minor

Dispose TextEditingControllers after the dialog closes.

hostController, portController, usernameController, and passwordController are allocated on every call to _showProxyConfigDialog and never disposed, leaking controllers each time the user opens the dialog. Wrap the await showDialog(...) in a try/finally that disposes all four.

♻️ Proposed fix
-    await showDialog<void>(
-      context: context,
-      builder: (dialogContext) => StatefulBuilder(
-        ...
-      ),
-    );
+    try {
+      await showDialog<void>(
+        context: context,
+        builder: (dialogContext) => StatefulBuilder(
+          ...
+        ),
+      );
+    } finally {
+      hostController.dispose();
+      portController.dispose();
+      usernameController.dispose();
+      passwordController.dispose();
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart`
around lines 240 - 416, The four TextEditingController instances
(hostController, portController, usernameController, passwordController) created
in _showProxyConfigDialog are never disposed; fix by disposing them on all
paths: after creating the controllers, if (!mounted) dispose each and return;
otherwise wrap the await showDialog(...) call in try { await showDialog(...); }
finally { hostController.dispose(); portController.dispose();
usernameController.dispose(); passwordController.dispose(); } so controllers are
always cleaned up even if the dialog is dismissed or an early return occurs.
sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart (1)

203-283: ⚠️ Potential issue | 🟠 Major

Invalid multi-file manifests still complete successfully.

Two paths let an invalid manifest end in downloadCompleted:

  1. totalFiles == 0 (empty multiFile.files) — the loop doesn't execute, then _updateModelLocalPath and ModelDownloadProgress.completed still fire, registering an empty directory as a usable model.
  2. A descriptor with url == null (line 212) is continued past; if every descriptor has a null URL, or some succeed and others silently skip, the model is still marked completed with missing files.

Fail fast in both cases so partial/empty models aren't registered.

🐛 Proposed fix
           final totalFiles = multiFile.files.length;
+          if (totalFiles == 0) {
+            throw StateError('Multi-file model has no files: $modelId');
+          }
           final totalModelBytes = model.downloadSize ?? 0;
           var cumulativeDownloaded = 0;
@@
             final fileUrl = descriptor.url;
             if (fileUrl == null) {
-              _logger.warning(
-                  'No URL for file descriptor: ${descriptor.destinationPath}');
-              continue;
+              throw StateError(
+                'No URL for file descriptor: ${descriptor.destinationPath}',
+              );
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart`
around lines 203 - 283, Detect and fail fast for empty or invalid multi-file
manifests: before yielding ModelDownloadProgress.started check if
multiFile.files.isEmpty and throw/log and yield a failed/aborted progress
instead of continuing; inside the for-loop change the handling of descriptor.url
(currently continuing on null) to immediately fail (throw an exception or yield
a failed ModelDownloadProgress) when descriptor.url == null so the loop aborts
and _updateModelLocalPath / ModelDownloadProgress.completed are not reached for
empty/partial downloads; refer to multiFile.files, descriptor.url,
ModelDownloadProgress.started, ModelDownloadProgress.completed, and
_updateModelLocalPath to locate and update the logic.
examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp (3)

147-149: ⚠️ Potential issue | 🟡 Minor

OnCreate() failure still leaks the created HWND.

If OnCreate() returns false, Create() returns false while the HWND from Line 137 remains alive (pointing at a half-initialized Win32Window), and window_handle_ is still set. Subsequent messages may route into the subclass whose setup failed, until the destructor eventually runs. Call DestroyWindow(window) / Destroy() on the failure path before returning false.

🐛 Proposed fix
   UpdateTheme(window);

-  return OnCreate();
+  if (!OnCreate()) {
+    Destroy();
+    return false;
+  }
+
+  return true;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp` around lines
147 - 149, If Create() sets window_handle_ and then calls OnCreate(), but
OnCreate() returns false you must clean up the partially-created HWND to avoid
leaking a half-initialized Win32Window; modify Create() so that when OnCreate()
returns false it calls DestroyWindow(window) (or this->Destroy()) and
clears/reset window_handle_ before returning false, referencing the existing
Create(), OnCreate(), window_handle_, DestroyWindow(window) and Destroy()
symbols to locate the fix.

224-231: ⚠️ Potential issue | 🟠 Major

DestroyWindow return value is still ignored.

DestroyWindow returns a BOOL; Line 226 discards it, Line 227 clears window_handle_ unconditionally, and Lines 229‑231 may unregister the class even when the window was not actually destroyed. Check the return, retain window_handle_ on failure, and only unregister the class on success.

🛠 Proposed fix
 void Win32Window::Destroy() {
   if (window_handle_) {
-    DestroyWindow(window_handle_);
-    window_handle_ = nullptr;
+    HWND window = window_handle_;
+    if (!DestroyWindow(window)) {
+      return;
+    }
+    if (window_handle_ == window) {
+      window_handle_ = nullptr;
+    }
   }
   if (g_active_window_count == 0) {
     WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp` around lines
224 - 231, In Win32Window::Destroy, stop ignoring DestroyWindow's BOOL return:
call BOOL destroyed = DestroyWindow(window_handle_); only set window_handle_ =
nullptr if destroyed is nonzero; if destroyed is zero, retain the handle and
optionally log or handle the failure; decrement/unregister the window class and
call WindowClassRegistrar::GetInstance()->UnregisterWindowClass() only when the
destroy actually succeeded and g_active_window_count indicates no active
windows. Ensure you reference window_handle_, DestroyWindow,
g_active_window_count, and
WindowClassRegistrar::GetInstance()->UnregisterWindowClass() in the fix.

181-221: ⚠️ Potential issue | 🟠 Major

DefWindowProc is still invoked with the cleared window_handle_, and WM_NCDESTROY is unhandled.

After WM_DESTROY zeros window_handle_ at Line 183, subsequent messages (notably WM_NCDESTROY) fall through to Line 221 and pass nullptr to DefWindowProc, which violates the Win32 API contract and skips default non-client cleanup. Use the hwnd parameter and add a WM_NCDESTROY case that clears GWLP_USERDATA:

🛠 Proposed fix
     case WM_DWMCOLORIZATIONCOLORCHANGED:
       UpdateTheme(hwnd);
       return 0;
+
+    case WM_NCDESTROY:
+      SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
+      return DefWindowProc(hwnd, message, wparam, lparam);
   }
 
-  return DefWindowProc(window_handle_, message, wparam, lparam);
+  return DefWindowProc(hwnd, message, wparam, lparam);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp` around lines
181 - 221, The code currently calls DefWindowProc(window_handle_, message,
wparam, lparam) after WM_DESTROY has cleared window_handle_, and it doesn't
handle WM_NCDESTROY; change the final fallback to call DefWindowProc(hwnd,
message, wparam, lparam) (use the incoming hwnd parameter, not window_handle_),
and add a WM_NCDESTROY case that clears the per-window user data (call
SetWindowLongPtr(hwnd, GWLP_USERDATA, 0)) before returning DefWindowProc(hwnd,
message, wparam, lparam) so default non-client cleanup runs correctly.
examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart (1)

27-56: ⚠️ Potential issue | 🟠 Major

End-to-end timeouts still missing on getJson and testProxy.

connectionTimeout = 15s at Line 72 only bounds TCP connect; request.close() and the body read at Lines 34‑35 / 51 can still hang indefinitely on a stalled proxy or server, blocking tool HTTP calls and the “Test proxy” UX path. Wrap request.close() and response.transform(utf8.decoder).join() with .timeout(...) (sharing a constant between both methods) and handle TimeoutException so the client.close(force: true) in finally still runs.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart`
around lines 27 - 56, Both getJson and testProxy need end-to-end timeouts:
define a shared Duration constant (e.g., requestTimeout) near your
connectionTimeout and wrap the async calls that can hang—specifically the
request.close() and, in getJson, the
response.transform(utf8.decoder).join()—with .timeout(requestTimeout) and catch
TimeoutException (from dart:async) to return a sensible failure (e.g.,
ExampleHttpResponse with an error status or false for testProxy); keep the
existing finally blocks so client.close(force: true) always runs.
🧹 Nitpick comments (12)
sdk/runanywhere-commons/src/infrastructure/extraction/rac_extraction_stub.cpp (1)

23-25: Prefer value-initialization over positional aggregate init for out_result.

*out_result = {0, 0, 0, 0}; hard-codes an assumption about the exact field count of rac_extraction_result_t. If a field is ever added, reordered, or changed to a non-scalar type, this will either silently skip zero-initializing the new field or fail to compile — neither of which is caught locally since this is a stub. Elsewhere in the codebase (e.g., download_orchestrator.cpp:494 and runanywhere_commons_jni.cpp:4433) the idiom rac_extraction_result_t result = {}; is used consistently. Match that convention here for resilience:

♻️ Proposed change
     if (out_result) {
-        *out_result = {0, 0, 0, 0};
+        *out_result = {};
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-commons/src/infrastructure/extraction/rac_extraction_stub.cpp`
around lines 23 - 25, The assignment *out_result = {0, 0, 0, 0}; assumes the
exact aggregate layout of rac_extraction_result_t and risks missing new fields;
replace it with value-initialization so new fields are zero-initialized safely —
e.g. use *out_result = {}; or construct a rac_extraction_result_t tmp = {};
*out_result = tmp; to initialize out_result via value-initialization
(referencing the out_result variable and rac_extraction_result_t type in the
stub).
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_auth.dart (1)

91-112: Nit: bodyLength is UTF-16 code units, not bytes.

String.length / response.body.length return UTF-16 code-unit counts, which diverge from the on-wire byte count once the payload contains non-ASCII (e.g., unicode device names or error messages). For diagnostic logs this is usually fine, but if downstream tooling assumes bytes, consider utf8.encode(requestJson).length (or response.bodyBytes.length) and perhaps rename to bodyChars otherwise.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_auth.dart`
around lines 91 - 112, The metadata helpers _authRequestLogMetadata and
_authResponseLogMetadata currently record bodyLength using String.length (UTF-16
code units); change them to report actual byte lengths by using utf8-encoding
for the request (use utf8.encode(requestJson).length) and
response.bodyBytes.length for the response, and update the metadata key to a
clear name like bodyBytes (or if you prefer to keep the original name, also add
a comment clarifying it is a byte count) so downstream tooling gets the on-wire
byte count.
examples/flutter/RunAnywhereAI/lib/core/services/platform_capability_service.dart (1)

16-19: Hard-coded "Windows vertical-slice" wording is unnecessary coupling.

unsupportedMessage always attributes the gap to the Windows vertical-slice build, but if more platform-specific capability flags are added in the future (e.g., iOS-only features), the message will become inaccurate. Consider deriving the feature label from the triggering condition rather than hard-coding a platform reference, so the message scales with additional gates.

Note: Platform.isWindows is safe here since Flutter web is not a supported target for this example app.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@examples/flutter/RunAnywhereAI/lib/core/services/platform_capability_service.dart`
around lines 16 - 19, The message in unsupportedMessage is hard-coded to
"Windows vertical-slice" which couples the text to one platform; update
unsupportedMessage and the supportsRag logic to produce a platform-derived
message instead (use Platform.operatingSystem or similar) so the text reflects
the actual runtime OS that gated the feature; modify unsupportedMessage(String
featureName) to return "$featureName is not enabled on
${Platform.operatingSystem}." (or an equivalent generic platform-derived phrase)
and ensure any checks referencing Platform.isWindows (e.g., supportsRag) remain
accurate but no longer dictate a hard-coded string.
examples/flutter/RunAnywhereAI/lib/features/rag/rag_demo_view.dart (1)

222-234: Initialization order: _viewModel created before capability check, but has minimal cost.

The RAGViewModel is instantiated in the field initializer (line 37) and the listener is registered in initState (line 55), both before the capability check in build() (line 224). However, the constructor does not touch the SDK—only field initializers allocate primitive state (strings, bools, lists) and extend ChangeNotifier. SDK operations (ragCreatePipeline, ragIngest, ragQuery) only occur inside method calls triggered by user actions (loadDocument, askQuestion), which are unreachable if supportsRag is false. The dispose() method correctly cleans up resources. If you want to avoid allocating the ViewModel at all on unsupported platforms, you could lazily instantiate it in build() only when needed, though the current pattern is acceptable.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/lib/features/rag/rag_demo_view.dart` around
lines 222 - 234, The RAGViewModel (_viewModel) is currently created in the field
initializer even when PlatformCapabilityService.shared.supportsRag is false; to
avoid allocating it on unsupported platforms, make _viewModel nullable, remove
the field initializer, and lazily instantiate it inside build() only when
capability.supportsRag is true (use PlatformCapabilityService.shared to check).
When you create the instance, register its listener (the same logic now in
initState) and ensure dispose() still removes the listener and disposes the view
model if non-null; keep RAGViewModel, _viewModel, initState, build, dispose, and
supportsRag as the reference points.
sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt (1)

119-131: Windows post-fetch compile tweaks look reasonable.

Gating target_compile_options(llama PRIVATE /Zc:char8_t-) and the common target downgrade to C++17 inside if(TARGET …) guards is correct, since FetchContent_MakeAvailable may not always produce those targets depending on llama.cpp options. One consideration: /Zc:char8_t- is MSVC-specific — if a Windows build ever uses clang-cl or MinGW, this flag will warn/fail. Consider guarding with if(MSVC) for robustness.

♻️ Optional: guard MSVC-only flag
 if(RAC_PLATFORM_WINDOWS)
-    if(TARGET llama)
+    if(MSVC AND TARGET llama)
         target_compile_options(llama PRIVATE /Zc:char8_t-)
     endif()
     if(TARGET common)
         set_target_properties(common PROPERTIES
             CXX_STANDARD 17
             CXX_STANDARD_REQUIRED ON
             CXX_EXTENSIONS OFF
         )
     endif()
 endif()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt` around lines
119 - 131, The post-fetch Windows tweaks add the MSVC-only flag /Zc:char8_t- via
target_compile_options(llama PRIVATE /Zc:char8_t-) which can break builds using
clang-cl or MinGW; wrap that call in an MSVC-specific guard (e.g., check
if(MSVC) before invoking target_compile_options for target "llama") while
leaving the target property adjustments for "common" unchanged so the C++17
downgrade still applies on Windows but the compiler-specific flag is only added
when MSVC is in use.
examples/flutter/RunAnywhereAI/lib/features/vision/vlm_view_model.dart (1)

32-36: _hasCameraDevice defaults to true before enumeration runs.

UI reading hasCameraDevice before initializeCamera() completes will briefly see true even on a device without cameras. Initializing to false (or a tri-state/null) avoids a momentarily misleading state in the view.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/lib/features/vision/vlm_view_model.dart`
around lines 32 - 36, _hasCameraDevice is initialized to true causing a brief
incorrect UI state; change its initial value to false (or null if using a
tri-state) and ensure initializeCamera() sets _hasCameraDevice based on actual
enumeration results and calls notifyListeners()/updates any public getter (e.g.,
hasCameraDevice) after detection so the view only sees the true value when a
camera is confirmed; update references to _hasCameraDevice and any getters to
handle the new initial state.
examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dart (2)

33-44: device.handle! as CameraDescription will throw if misused.

VisionCameraBackend.createSession is part of the public surface; a VisionCameraDevice constructed from a test fake, another backend, or any code path that omits handle will hit a null-assertion crash rather than a helpful error. Consider a typed check with a clear exception:

♻️ Proposed change
   `@override`
   VisionCameraSession createSession(VisionCameraDevice device) {
-    final description = device.handle! as CameraDescription;
+    final handle = device.handle;
+    if (handle is! CameraDescription) {
+      throw ArgumentError(
+        'CameraPluginVisionCameraBackend requires a device produced by its own '
+        'listDevices(); got handle of type ${handle.runtimeType}.',
+      );
+    }
     return _CameraPluginVisionCameraSession(
       CameraController(
-        description,
+        handle,
         ResolutionPreset.medium,
         imageFormatGroup: ImageFormatGroup.bgra8888,
       ),
     );
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dart`
around lines 33 - 44, CameraPluginVisionCameraBackend.createSession currently
force-unwraps device.handle which can cause a null-assertion crash; update
createSession to validate that device.handle is non-null and of type
CameraDescription before using it (check device.handle != null && device.handle
is CameraDescription), and throw a clear exception (e.g.,
ArgumentError/StateError) with context if the check fails so callers get a
helpful error instead of a runtime crash; then cast to CameraDescription and
proceed to construct the CameraController and return the existing
_CameraPluginVisionCameraSession as before.

4-4: VisionCameraLensDirection.unknown is defined but never produced.

The switch at lines 54–58 maps all three current CameraLensDirection values explicitly, leaving unknown as dead code. If the camera package adds a new enum value, the exhaustive switch will fail at compile-time until this code is updated. Consider either removing unknown or adding a catch-all case for forward compatibility.

♻️ Proposed change
             lensDirection: switch (camera.lensDirection) {
               CameraLensDirection.front => VisionCameraLensDirection.front,
               CameraLensDirection.back => VisionCameraLensDirection.back,
               CameraLensDirection.external => VisionCameraLensDirection.external,
+              _ => VisionCameraLensDirection.unknown,
             },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dart`
at line 4, The VisionCameraLensDirection enum contains an "unknown" value that's
never produced by the current mapping from the camera package's
CameraLensDirection; update the mapping code that converts CameraLensDirection
(the switch at lines 54–58) to include a catch-all branch (default/fallback)
that returns VisionCameraLensDirection.unknown for unexpected/new
CameraLensDirection values, or remove the unknown member if you prefer strict
mapping—modify the switch in the conversion function so it explicitly handles
front/back/external and uses a default case to map any other value to
VisionCameraLensDirection.unknown for forward compatibility.
sdk/runanywhere-flutter/packages/runanywhere/windows/CMakeLists.txt (2)

38-41: Hard-coded x64 path won't match ARM64 Windows builds.

Both candidate paths bake in windows/x64/rac_commons.dll. Flutter desktop on Windows supports ARM64, and CMake already exposes the target architecture (e.g., CMAKE_SYSTEM_PROCESSOR / CMAKE_GENERATOR_PLATFORM). If someone builds the app for ARM64, configuration will hard-fail with a misleading error even if an ARM64 rac_commons.dll is available. Consider deriving the arch segment from the build configuration so the same CMakeLists works for both.

💡 Sketch of an arch-aware lookup
-set(COMMONS_DLL_CANDIDATES
-  "${REPO_ROOT_FROM_APP}/sdk/runanywhere-commons/dist/windows/x64/rac_commons.dll"
-  "${PACKAGE_ROOT}/../../../runanywhere-commons/dist/windows/x64/rac_commons.dll"
-)
+if(CMAKE_GENERATOR_PLATFORM MATCHES "ARM64|arm64")
+  set(_RAC_ARCH "arm64")
+else()
+  set(_RAC_ARCH "x64")
+endif()
+set(COMMONS_DLL_CANDIDATES
+  "${REPO_ROOT_FROM_APP}/sdk/runanywhere-commons/dist/windows/${_RAC_ARCH}/rac_commons.dll"
+  "${PACKAGE_ROOT}/../../../runanywhere-commons/dist/windows/${_RAC_ARCH}/rac_commons.dll"
+)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-flutter/packages/runanywhere/windows/CMakeLists.txt` around
lines 38 - 41, The hard-coded "windows/x64" path in COMMONS_DLL_CANDIDATES will
fail for ARM64 builds; update CMakeLists to compute an architecture token (e.g.,
set ARCH to "x64" or "arm64" by inspecting CMAKE_SYSTEM_PROCESSOR and
CMAKE_GENERATOR_PLATFORM) and use that token in the candidate paths instead of
the literal "x64" so COMMONS_DLL_CANDIDATES points to
"${REPO_ROOT_FROM_APP}/sdk/runanywhere-commons/dist/windows/${ARCH}/rac_commons.dll"
and the PACKAGE_ROOT alternative; ensure the ARCH detection covers common values
like "AMD64"/"x86_64" → x64 and "ARM64"/"aarch64" → arm64 and falls back to x64
if unknown.

35-55: Relative path from CMAKE_SOURCE_DIR is fragile for out-of-monorepo consumers.

REPO_ROOT_FROM_APP is computed as ${CMAKE_SOURCE_DIR}/../../../../. When the plugin is built via the in-repo example app (examples/flutter/RunAnywhereAI/windows), this correctly resolves to the monorepo root. However, if this package is ever consumed by a Flutter app outside the monorepo (e.g., after publishing), CMAKE_SOURCE_DIR will point to that app's windows/ folder and the computed path will be nonsense — only the second PACKAGE_ROOT-based candidate can succeed.

The FATAL_ERROR at Line 52 also echoes the raw candidate list and tells users to run scripts/build-windows.bat, a path that only exists inside this monorepo. For now this is acceptable (the plugin is consumed only from within the repo), but please add a README note and/or guard the REPO_ROOT_FROM_APP candidate behind a sanity check (e.g., verify sdk/runanywhere-commons exists at that path) before shipping externally.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-flutter/packages/runanywhere/windows/CMakeLists.txt` around
lines 35 - 55, The REPO_ROOT_FROM_APP candidate is fragile when CMAKE_SOURCE_DIR
points at an external app; before adding
"${REPO_ROOT_FROM_APP}/sdk/runanywhere-commons/..." to COMMONS_DLL_CANDIDATES,
check that the directory "${REPO_ROOT_FROM_APP}/sdk/runanywhere-commons"
actually exists and only then append that candidate (leave the
PACKAGE_ROOT-based candidate untouched); also update the message(FATAL_ERROR)
call to avoid echoing internal repo-only paths or instructing users to run
scripts/build-windows.bat (use a generic message like "Missing rac_commons.dll;
ensure runanywhere-commons is available or built") and add a short README note
explaining that the REPO_ROOT_FROM_APP lookup is monorepo-specific and consumers
must provide the DLL or build runanywhere-commons.
examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart (1)

123-131: Consider moving configureDownloadHttpClientFactory earlier, before module registration, as defensive programming.

Currently, configureDownloadHttpClientFactory is installed at line 123 — after _registerModulesAndModels() (line 105) and before ModelManager.shared.refresh() (line 131). While refresh() only notifies listeners and performs no network I/O, and addModel/registerModel currently persist metadata without triggering downloads, moving the factory configuration to run immediately after RunAnywhere.initialize() would make the code more robust against future changes to model registration or discovery logic.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart` around lines
123 - 131, Move the RunAnywhere.configureDownloadHttpClientFactory call to
immediately after RunAnywhere.initialize() and before
_registerModulesAndModels() so the download HTTP client factory is set up before
any module/model registration or discovery; update the placement of the
configureDownloadHttpClientFactory invocation (currently near
ModelManager.shared.refresh()) to directly follow RunAnywhere.initialize() to
defensively ensure downloads use the configured factory during
registerModel/addModel or any future changes to ModelManager.shared.refresh().
examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart (1)

90-104: Prefer pre-registering proxy credentials over authenticateProxy callback.

authenticateProxy is invoked on each 407 challenge; since the proxy host/port are already known from settings, calling client.addProxyCredentials(settings.host.trim(), settings.port!, '', HttpClientBasicCredentials(...)) once after findProxy is set is simpler and avoids re-adding credentials on every challenge. The callback form is also brittle if the proxy advertises a realm that differs from ''. Functionally not broken, but cleaner.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart`
around lines 90 - 104, Replace the per-407 authenticateProxy callback with a
one-time pre-registration of proxy credentials: after you set findProxy, if
settings.username is not empty and settings.port is not null, call
client.addProxyCredentials(settings.host.trim(), settings.port!, '',
HttpClientBasicCredentials(settings.username, settings.password)) once (and
remove the client.authenticateProxy = ... block); this avoids re-adding
credentials on every 407 and prevents brittle behavior when the proxy realm
differs from '' while keeping the same credential values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1ffcdb3b-912b-48be-9a91-d9c6c247d7e4

📥 Commits

Reviewing files that changed from the base of the PR and between 67826b3 and c29f567.

⛔ Files ignored due to path filters (1)
  • examples/flutter/RunAnywhereAI/windows/runner/resources/app_icon.ico is excluded by !**/*.ico
📒 Files selected for processing (99)
  • .gitignore
  • docs/building.md
  • examples/flutter/RunAnywhereAI/.gitignore
  • examples/flutter/RunAnywhereAI/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java
  • examples/flutter/RunAnywhereAI/ios/Runner/GeneratedPluginRegistrant.m
  • examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart
  • examples/flutter/RunAnywhereAI/lib/core/design_system/app_colors.dart
  • examples/flutter/RunAnywhereAI/lib/core/design_system/unsupported_feature_view.dart
  • examples/flutter/RunAnywhereAI/lib/core/models/proxy_settings.dart
  • examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart
  • examples/flutter/RunAnywhereAI/lib/core/services/platform_capability_service.dart
  • examples/flutter/RunAnywhereAI/lib/core/services/proxy_settings_service.dart
  • examples/flutter/RunAnywhereAI/lib/core/utilities/constants.dart
  • examples/flutter/RunAnywhereAI/lib/features/chat/chat_interface_view.dart
  • examples/flutter/RunAnywhereAI/lib/features/models/add_model_from_url_view.dart
  • examples/flutter/RunAnywhereAI/lib/features/models/model_list_view_model.dart
  • examples/flutter/RunAnywhereAI/lib/features/models/model_selection_sheet.dart
  • examples/flutter/RunAnywhereAI/lib/features/rag/rag_demo_view.dart
  • examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart
  • examples/flutter/RunAnywhereAI/lib/features/settings/tool_settings_view_model.dart
  • examples/flutter/RunAnywhereAI/lib/features/structured_output/structured_output_view.dart
  • examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dart
  • examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_permission_gateway.dart
  • examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_vlm_service.dart
  • examples/flutter/RunAnywhereAI/lib/features/vision/vision_hub_view.dart
  • examples/flutter/RunAnywhereAI/lib/features/vision/vlm_camera_view.dart
  • examples/flutter/RunAnywhereAI/lib/features/vision/vlm_view_model.dart
  • examples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dart
  • examples/flutter/RunAnywhereAI/lib/features/voice/text_to_speech_view.dart
  • examples/flutter/RunAnywhereAI/lib/features/voice/voice_assistant_view.dart
  • examples/flutter/RunAnywhereAI/pubspec.yaml
  • examples/flutter/RunAnywhereAI/test/core/models/proxy_settings_test.dart
  • examples/flutter/RunAnywhereAI/test/core/services/example_http_service_test.dart
  • examples/flutter/RunAnywhereAI/test/core/services/proxy_settings_service_test.dart
  • examples/flutter/RunAnywhereAI/test/features/dropdown_form_field_compatibility_test.dart
  • examples/flutter/RunAnywhereAI/test/features/vision/vlm_view_model_test.dart
  • examples/flutter/RunAnywhereAI/test/widget_test.dart
  • examples/flutter/RunAnywhereAI/windows/.gitignore
  • examples/flutter/RunAnywhereAI/windows/CMakeLists.txt
  • examples/flutter/RunAnywhereAI/windows/flutter/CMakeLists.txt
  • examples/flutter/RunAnywhereAI/windows/flutter/generated_plugin_registrant.cc
  • examples/flutter/RunAnywhereAI/windows/flutter/generated_plugin_registrant.h
  • examples/flutter/RunAnywhereAI/windows/flutter/generated_plugins.cmake
  • examples/flutter/RunAnywhereAI/windows/runner/CMakeLists.txt
  • examples/flutter/RunAnywhereAI/windows/runner/Runner.rc
  • examples/flutter/RunAnywhereAI/windows/runner/flutter_window.cpp
  • examples/flutter/RunAnywhereAI/windows/runner/flutter_window.h
  • examples/flutter/RunAnywhereAI/windows/runner/main.cpp
  • examples/flutter/RunAnywhereAI/windows/runner/resource.h
  • examples/flutter/RunAnywhereAI/windows/runner/runner.exe.manifest
  • examples/flutter/RunAnywhereAI/windows/runner/utils.cpp
  • examples/flutter/RunAnywhereAI/windows/runner/utils.h
  • examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp
  • examples/flutter/RunAnywhereAI/windows/runner/win32_window.h
  • sdk/runanywhere-commons/CMakeLists.txt
  • sdk/runanywhere-commons/README.md
  • sdk/runanywhere-commons/scripts/build-windows.bat
  • sdk/runanywhere-commons/scripts/windows/download-sherpa-onnx.bat
  • sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt
  • sdk/runanywhere-commons/src/backends/onnx/CMakeLists.txt
  • sdk/runanywhere-commons/src/backends/onnx/onnx_backend.cpp
  • sdk/runanywhere-commons/src/backends/onnx/rac_backend_onnx_register.cpp
  • sdk/runanywhere-commons/src/core/rac_core.cpp
  • sdk/runanywhere-commons/src/infrastructure/download/download_orchestrator.cpp
  • sdk/runanywhere-commons/src/infrastructure/extraction/rac_extraction_stub.cpp
  • sdk/runanywhere-commons/tests/test_download_orchestrator.cpp
  • sdk/runanywhere-flutter/packages/runanywhere/README.md
  • sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/internal/vlm_file_resolution.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_auth.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_model_registry.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_stt.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_tts.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/native/platform_loader.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart
  • sdk/runanywhere-flutter/packages/runanywhere/pubspec.yaml
  • sdk/runanywhere-flutter/packages/runanywhere/test/auth_sdk_config_ffi_layout_test.dart
  • sdk/runanywhere-flutter/packages/runanywhere/test/dart_bridge_auth_test.dart
  • sdk/runanywhere-flutter/packages/runanywhere/test/download_service_test.dart
  • sdk/runanywhere-flutter/packages/runanywhere/test/vlm_file_resolution_test.dart
  • sdk/runanywhere-flutter/packages/runanywhere/windows/CMakeLists.txt
  • sdk/runanywhere-flutter/packages/runanywhere/windows/include/runanywhere/runanywhere_plugin_c_api.h
  • sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin.cpp
  • sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin.h
  • sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin_c_api.cpp
  • sdk/runanywhere-flutter/packages/runanywhere_llamacpp/lib/native/llamacpp_bindings.dart
  • sdk/runanywhere-flutter/packages/runanywhere_llamacpp/pubspec.yaml
  • sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/CMakeLists.txt
  • sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/include/runanywhere_llamacpp/runanywhere_llamacpp_plugin_c_api.h
  • sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.cpp
  • sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.h
  • sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin_c_api.cpp
  • sdk/runanywhere-flutter/packages/runanywhere_onnx/lib/native/onnx_bindings.dart
  • sdk/runanywhere-flutter/packages/runanywhere_onnx/pubspec.yaml
  • sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/CMakeLists.txt
  • sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/include/runanywhere_onnx/runanywhere_onnx_plugin_c_api.h
  • sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin.cpp
  • sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin.h
  • sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin_c_api.cpp
💤 Files with no reviewable changes (1)
  • examples/flutter/RunAnywhereAI/.gitignore
✅ Files skipped from review due to trivial changes (24)
  • .gitignore
  • sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_tts.dart
  • examples/flutter/RunAnywhereAI/windows/runner/runner.exe.manifest
  • examples/flutter/RunAnywhereAI/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java
  • examples/flutter/RunAnywhereAI/windows/.gitignore
  • sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_stt.dart
  • examples/flutter/RunAnywhereAI/lib/features/structured_output/structured_output_view.dart
  • examples/flutter/RunAnywhereAI/test/core/models/proxy_settings_test.dart
  • examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_permission_gateway.dart
  • examples/flutter/RunAnywhereAI/windows/runner/resource.h
  • examples/flutter/RunAnywhereAI/lib/core/design_system/unsupported_feature_view.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_model_registry.dart
  • sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin_c_api.cpp
  • sdk/runanywhere-flutter/packages/runanywhere_llamacpp/pubspec.yaml
  • sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin_c_api.cpp
  • examples/flutter/RunAnywhereAI/windows/runner/Runner.rc
  • sdk/runanywhere-commons/README.md
  • docs/building.md
  • examples/flutter/RunAnywhereAI/test/features/dropdown_form_field_compatibility_test.dart
  • examples/flutter/RunAnywhereAI/lib/core/utilities/constants.dart
  • examples/flutter/RunAnywhereAI/windows/flutter/generated_plugins.cmake
  • examples/flutter/RunAnywhereAI/lib/features/models/model_selection_sheet.dart
  • examples/flutter/RunAnywhereAI/windows/runner/CMakeLists.txt
  • sdk/runanywhere-commons/src/infrastructure/download/download_orchestrator.cpp
🚧 Files skipped from review as they are similar to previous changes (33)
  • sdk/runanywhere-flutter/packages/runanywhere/pubspec.yaml
  • sdk/runanywhere-commons/CMakeLists.txt
  • sdk/runanywhere-flutter/packages/runanywhere_onnx/pubspec.yaml
  • sdk/runanywhere-flutter/packages/runanywhere/test/auth_sdk_config_ffi_layout_test.dart
  • examples/flutter/RunAnywhereAI/lib/features/models/add_model_from_url_view.dart
  • sdk/runanywhere-flutter/packages/runanywhere_llamacpp/lib/native/llamacpp_bindings.dart
  • examples/flutter/RunAnywhereAI/test/core/services/example_http_service_test.dart
  • examples/flutter/RunAnywhereAI/pubspec.yaml
  • examples/flutter/RunAnywhereAI/test/widget_test.dart
  • sdk/runanywhere-flutter/packages/runanywhere/README.md
  • sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.cpp
  • sdk/runanywhere-commons/tests/test_download_orchestrator.cpp
  • sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin_c_api.cpp
  • sdk/runanywhere-flutter/packages/runanywhere/test/vlm_file_resolution_test.dart
  • sdk/runanywhere-flutter/packages/runanywhere/test/download_service_test.dart
  • examples/flutter/RunAnywhereAI/test/core/services/proxy_settings_service_test.dart
  • sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin.cpp
  • examples/flutter/RunAnywhereAI/lib/core/design_system/app_colors.dart
  • sdk/runanywhere-commons/src/backends/onnx/CMakeLists.txt
  • examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_vlm_service.dart
  • examples/flutter/RunAnywhereAI/lib/features/voice/voice_assistant_view.dart
  • examples/flutter/RunAnywhereAI/windows/runner/utils.cpp
  • examples/flutter/RunAnywhereAI/test/features/vision/vlm_view_model_test.dart
  • sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin.cpp
  • sdk/runanywhere-commons/src/backends/onnx/rac_backend_onnx_register.cpp
  • examples/flutter/RunAnywhereAI/lib/features/voice/text_to_speech_view.dart
  • sdk/runanywhere-commons/src/core/rac_core.cpp
  • examples/flutter/RunAnywhereAI/lib/core/models/proxy_settings.dart
  • sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/CMakeLists.txt
  • sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart
  • examples/flutter/RunAnywhereAI/lib/features/vision/vlm_camera_view.dart
  • examples/flutter/RunAnywhereAI/windows/flutter/CMakeLists.txt
  • examples/flutter/RunAnywhereAI/lib/features/chat/chat_interface_view.dart

Comment on lines +367 to 378
debugPrint('✅ ONNX backend registered (Windows optional)');
} catch (e) {
debugPrint('⚠️ ONNX backend not available: $e');
debugPrint('⚠️ ONNX backend not available on this Windows build: $e');
}

// --- RAG BACKEND ---
try {
await RAGModule.register();
debugPrint('✅ RAG backend registered');
} catch (e) {
debugPrint('⚠️ RAG backend not available (RAG features disabled): $e');
debugPrint('⚠️ RAG backend not available on this Windows build: $e');
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Platform-specific wording in log messages will be misleading on non-Windows builds.

These debugPrint strings hard-code "Windows" even though _registerModulesAndModels runs on every target platform (iOS/Android/macOS/Linux/Windows). If Onnx.register() or RAGModule.register() fails on, say, Linux or Android, the log will incorrectly say "not available on this Windows build", which will make production/CI debugging harder. Similarly, "ONNX backend registered (Windows optional)" on Line 367 is confusing in the success path on every other platform.

Consider either platform-gating these messages or making them platform-agnostic.

📝 Suggested rewording
     try {
       await Onnx.register();
-      debugPrint('✅ ONNX backend registered (Windows optional)');
+      debugPrint('✅ ONNX backend registered');
     } catch (e) {
-      debugPrint('⚠️ ONNX backend not available on this Windows build: $e');
+      debugPrint('⚠️ ONNX backend not available on this platform: $e');
     }

     // --- RAG BACKEND ---
     try {
       await RAGModule.register();
       debugPrint('✅ RAG backend registered');
     } catch (e) {
-      debugPrint('⚠️ RAG backend not available on this Windows build: $e');
+      debugPrint('⚠️ RAG backend not available on this platform: $e');
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart` around lines
367 - 378, Log messages in _registerModulesAndModels hard-code "Windows" causing
misleading output when Onnx.register() or RAGModule.register() runs on other
OSes; update the catch and success debugPrints for Onnx.register() and
RAGModule.register() to be platform-agnostic (or gate them behind a
Platform.isWindows check) and include actual platform info when available (e.g.,
use Platform.operatingSystem) so success messages like "ONNX backend registered"
and failure messages do not incorrectly state "on this Windows build."

Comment on lines 56 to 108
Future<void> initializeCamera() async {
try {
final cameras = await availableCameras();
if (cameras.isEmpty) {
debugPrint('❌ No cameras available');
final devices = await _cameraBackend.listDevices();
if (_isDisposed) {
return;
}
if (devices.isEmpty) {
_hasCameraDevice = false;
_isCameraInitialized = false;
_error = 'No cameras available on this device.';
_safeNotifyListeners();
return;
}

// Select back camera (or first available)
final camera = cameras.firstWhere(
(c) => c.lensDirection == CameraLensDirection.back,
orElse: () => cameras.first,
);

// Create controller with BGRA format request (iOS preferred, Android fallback to YUV)
_cameraController = CameraController(
camera,
ResolutionPreset.medium,
imageFormatGroup: ImageFormatGroup.bgra8888,
_hasCameraDevice = true;
final device = devices.firstWhere(
(camera) => camera.lensDirection == VisionCameraLensDirection.back,
orElse: () => devices.first,
);

await _cameraController!.initialize();
await _cameraSession?.dispose();
if (_isDisposed) {
return;
}
final nextSession = _cameraBackend.createSession(device);
if (_isDisposed) {
unawaited(nextSession.dispose());
return;
}
try {
await nextSession.initialize();
} catch (_) {
await nextSession.dispose();
rethrow;
}
if (_isDisposed) {
unawaited(nextSession.dispose());
return;
}
_cameraSession = nextSession;

_isCameraInitialized = true;
notifyListeners();

debugPrint('✅ Camera initialized: ${camera.lensDirection}');
_error = null;
_safeNotifyListeners();
} catch (e) {
debugPrint('❌ Camera initialization failed: $e');
if (_isDisposed) {
return;
}
_isCameraInitialized = false;
_error = 'Failed to initialize camera: $e';
notifyListeners();
_safeNotifyListeners();
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Race in initializeCamera: concurrent calls can orphan camera sessions.

If initializeCamera() is re-entered (e.g., rapid retry after permission grant, hot-reload, or UI re-mount) while a prior call is still awaiting listDevices() or nextSession.initialize():

  1. Call A builds nextSessionA but hasn't yet reached the _cameraSession = nextSession assignment at line 95.
  2. Call B's await _cameraSession?.dispose() at line 76 sees _cameraSession == null and does nothing.
  3. Both calls create and initialize their own sessions. Whichever assigns last wins; the other nextSession is leaked — its CameraController is never disposed, holding the OS camera handle open.

On iOS and especially Windows this will commonly manifest as a subsequent "camera in use" error. Guard the method against reentry or supersede in-flight initialization.

🔒 Proposed guard
   bool _hasCameraDevice = true;
   bool _isDisposed = false;
+  int _cameraInitToken = 0;

   VisionCameraSession? _cameraSession;
   Timer? _autoStreamTimer;
@@
   Future<void> initializeCamera() async {
+    final token = ++_cameraInitToken;
     try {
       final devices = await _cameraBackend.listDevices();
-      if (_isDisposed) {
+      if (_isDisposed || token != _cameraInitToken) {
         return;
       }
@@
-      await _cameraSession?.dispose();
-      if (_isDisposed) {
-        return;
-      }
       final nextSession = _cameraBackend.createSession(device);
-      if (_isDisposed) {
+      if (_isDisposed || token != _cameraInitToken) {
         unawaited(nextSession.dispose());
         return;
       }
       try {
         await nextSession.initialize();
       } catch (_) {
         await nextSession.dispose();
         rethrow;
       }
-      if (_isDisposed) {
+      if (_isDisposed || token != _cameraInitToken) {
         unawaited(nextSession.dispose());
         return;
       }
+      final previous = _cameraSession;
       _cameraSession = nextSession;
+      unawaited(previous?.dispose());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/lib/features/vision/vlm_view_model.dart`
around lines 56 - 108, initializeCamera can be re-entered and leak
CameraSessions; introduce a reentrancy guard (e.g., a private field like
_cameraInitToken or bool _isInitializingCamera) and use it to serialize or
supersede in-flight initializations: when starting initializeCamera() capture a
new token (or set _isInitializingCamera) and store it locally; before
creating/initializing sessions check that the current token still matches (or
_isInitializingCamera is still yours), and if not dispose any created
nextSession immediately and return; when assigning _cameraSession, dispose the
previous _cameraSession first and then set _cameraSession = nextSession only if
the token still matches; clear/reset the guard (_isInitializingCamera or token)
on normal completion or error, and ensure all early-return paths dispose newly
created sessions to avoid orphaning.

Comment on lines 195 to 230
Future<void> describePickedImage(String imagePath) async {
if (_isDisposed) {
return;
}
_isProcessing = true;
_error = null;
_currentDescription = '';
notifyListeners();
_safeNotifyListeners();

try {
// Create VLMImage from file path
final image = sdk.VLMImage.filePath(imagePath);

// Process image with streaming (more detailed prompt)
final result = await sdk.RunAnywhere.processImageStream(
image,
final tokens = _vlmService.processImageStream(
imagePath,
prompt: 'Describe this image in detail.',
options: const sdk.VLMGenerationOptions(maxTokens: 300),
maxTokens: 300,
);

// Listen to stream and append tokens
final buffer = StringBuffer(_currentDescription);
await for (final token in result.stream) {
final buffer = StringBuffer();
await for (final token in tokens) {
if (_isDisposed) {
return;
}
buffer.write(token);
_currentDescription = buffer.toString();
notifyListeners();
_safeNotifyListeners();
}

debugPrint('✅ Gallery photo described: ${_currentDescription.length} chars');
} catch (e) {
debugPrint('❌ Gallery photo error: $e');
if (_isDisposed) {
return;
}
_error = e.toString();
notifyListeners();
_safeNotifyListeners();
} finally {
_isProcessing = false;
notifyListeners();
_safeNotifyListeners();
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

describePickedImage lacks an _isProcessing guard.

Unlike describeCurrentFrame (line 154) and _describeCurrentFrameForAutoStream (line 256), this path will start a second VLM stream while one is already in flight (e.g., a user picks a new image while auto-streaming or while a previous description is still streaming). Both streams then interleave writes into _currentDescription, producing garbled output, and only one _isProcessing = false in the last finally wins.

Consider gating the entry and/or cancelling in-flight generation first:

🛡️ Proposed guard
   Future<void> describePickedImage(String imagePath) async {
-    if (_isDisposed) {
+    if (_isDisposed || _isProcessing) {
       return;
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Future<void> describePickedImage(String imagePath) async {
if (_isDisposed) {
return;
}
_isProcessing = true;
_error = null;
_currentDescription = '';
notifyListeners();
_safeNotifyListeners();
try {
// Create VLMImage from file path
final image = sdk.VLMImage.filePath(imagePath);
// Process image with streaming (more detailed prompt)
final result = await sdk.RunAnywhere.processImageStream(
image,
final tokens = _vlmService.processImageStream(
imagePath,
prompt: 'Describe this image in detail.',
options: const sdk.VLMGenerationOptions(maxTokens: 300),
maxTokens: 300,
);
// Listen to stream and append tokens
final buffer = StringBuffer(_currentDescription);
await for (final token in result.stream) {
final buffer = StringBuffer();
await for (final token in tokens) {
if (_isDisposed) {
return;
}
buffer.write(token);
_currentDescription = buffer.toString();
notifyListeners();
_safeNotifyListeners();
}
debugPrint('✅ Gallery photo described: ${_currentDescription.length} chars');
} catch (e) {
debugPrint('❌ Gallery photo error: $e');
if (_isDisposed) {
return;
}
_error = e.toString();
notifyListeners();
_safeNotifyListeners();
} finally {
_isProcessing = false;
notifyListeners();
_safeNotifyListeners();
}
}
Future<void> describePickedImage(String imagePath) async {
if (_isDisposed || _isProcessing) {
return;
}
_isProcessing = true;
_error = null;
_currentDescription = '';
_safeNotifyListeners();
try {
final tokens = _vlmService.processImageStream(
imagePath,
prompt: 'Describe this image in detail.',
maxTokens: 300,
);
final buffer = StringBuffer();
await for (final token in tokens) {
if (_isDisposed) {
return;
}
buffer.write(token);
_currentDescription = buffer.toString();
_safeNotifyListeners();
}
} catch (e) {
if (_isDisposed) {
return;
}
_error = e.toString();
_safeNotifyListeners();
} finally {
_isProcessing = false;
_safeNotifyListeners();
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/lib/features/vision/vlm_view_model.dart`
around lines 195 - 230, describePickedImage can start a new VLM stream while
another is active, causing interleaved writes to _currentDescription; add an
entry guard and/or cancel the in-flight generation before starting: at the top
of describePickedImage check _isProcessing and return early (like
describeCurrentFrame), or call the same cancellation/stop routine used by
_describeCurrentFrameForAutoStream to abort the existing stream before creating
tokens; ensure any canceled stream sets _isProcessing to false and that
describePickedImage sets _isProcessing = true only after the previous stream is
confirmed stopped to avoid concurrent writes to _currentDescription.

Comment on lines +123 to 138
Future<void> _refreshModelState() async {
final currentModel = await sdk.RunAnywhere.currentSTTModel();
if (!mounted) return;

setState(() {
_isProcessing = true;
_selectedFramework = currentModel == null
? LLMFramework.unknown
: switch (currentModel.framework) {
sdk.InferenceFramework.onnx => LLMFramework.onnxRuntime,
_ => LLMFramework.unknown,
};
_selectedModelName = currentModel?.name;
_supportsLiveMode = false;
_errorMessage = null;
});

try {
debugPrint('🔄 Loading STT model: ${model.name}');

// Load STT model directly via SDK (matches Swift: RunAnywhere.loadSTTModel)
await sdk.RunAnywhere.loadSTTModel(model.id);

setState(() {
_selectedFramework =
model.preferredFramework ?? LLMFramework.whisperKit;
_selectedModelName = model.name;
// WhisperKit supports live mode, ONNX may have limitations
_supportsLiveMode = model.preferredFramework == LLMFramework.whisperKit;
_isProcessing = false;
});

debugPrint('✅ STT model loaded: ${model.name}');
} catch (e) {
debugPrint('❌ Failed to load STT model: $e');
setState(() {
_errorMessage = 'Failed to load model: $e';
_isProcessing = 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.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# List InferenceFramework enum cases and how STT models are tagged.
ast-grep --pattern 'enum InferenceFramework { $$$ }'
rg -nP --type=dart -C2 '\bInferenceFramework\.' sdk/runanywhere-flutter | head -n 80
# Find STT model registrations to see which frameworks they use.
rg -nP --type=dart -C3 'ModelCategory\.stt|category:\s*ModelCategory\.stt' | head -n 60

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 13584


🏁 Script executed:

# Find STT model definitions and their frameworks
rg -nP --type=dart 'ModelCategory\.stt|\.stt\b' --max-count=50

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 4081


🏁 Script executed:

# Find LLMFramework enum definition
fd -e dart -x rg -l 'enum LLMFramework' {}

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 144


🏁 Script executed:

# Check git history for the _refreshModelState method to see prior implementation
git log -p --all -S '_refreshModelState' -- '*.dart' | head -200

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 56


🏁 Script executed:

# Search for _supportsLiveMode usage to understand its impact
rg -nP --type=dart '_supportsLiveMode' -B 2 -A 2

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 1685


🏁 Script executed:

# Read the LLMFramework enum definition
cat -n examples/flutter/RunAnywhereAI/lib/features/models/model_types.dart | head -100

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 3144


🏁 Script executed:

# Search for STT model definitions with framework values
rg -nP --type=dart '(ModelSelectionContext\.stt|category.*stt)' -B 5 -A 10 examples/flutter/RunAnywhereAI | head -150

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 14747


🏁 Script executed:

# Look for model registrations that might include STT models
rg -nP --type=dart 'framework:.*InferenceFramework' -B 2 -A 2 | head -100

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 8283


🏁 Script executed:

# Search for foundationModels in the context of speech/STT
rg -nP --type=dart 'foundationModels' -B 3 -A 3 | head -80

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 9377


🏁 Script executed:

# Search for whiskerKit/whisper framework references
rg -nP --type=dart -i 'whisper' | head -50

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 5379


🏁 Script executed:

# Check if there are model definitions specifically mapping STT to frameworks
fd -e dart -x grep -l 'speechRecognition\|stt' {} \; | head -10 | xargs -I {} sh -c 'echo "=== {} ===" && cat -n {} | grep -A 5 -B 5 framework'

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 15905


Extend the framework switch to handle all possible STT frameworks, not just ONNX.

The switch maps only sdk.InferenceFramework.onnx → LLMFramework.onnxRuntime; other frameworks like foundationModels and systemTTS (which exist in the SDK enum) fall through to LLMFramework.unknown. The chat interface uses a more complete pattern—add those mappings here as well. Follow the pattern in chat_interface_view.dart (lines 557–565) which handles llamaCpp, foundationModels, systemTTS, etc.

Also, _supportsLiveMode is unconditionally set to false regardless of the loaded model's capabilities. Either derive this from the model/framework or add a comment explaining why batch-only mode is required.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dart`
around lines 123 - 138, Update _refreshModelState to handle all
sdk.InferenceFramework cases instead of only onnx: in the switch over
currentModel.framework (inside _refreshModelState) add branches for
sdk.InferenceFramework.llamaCpp → LLMFramework.llamaCpp,
sdk.InferenceFramework.foundationModels → LLMFramework.foundationModels,
sdk.InferenceFramework.systemTTS → LLMFramework.systemTTS (and any other enum
members present) following the mapping pattern used in chat_interface_view.dart;
do not leave other frameworks falling back to unknown. Also stop unconditionally
setting _supportsLiveMode = false: derive it from the model if a capability flag
exists (e.g., currentModel.supportsLiveMode or currentModel.capabilities) or, if
no such field exists, add a short comment above the assignment explaining why
this view is batch-only and keep the code consistent with
_selectedFramework/_selectedModelName handling in _refreshModelState.

elseif(RAC_PLATFORM_WINDOWS)
message(STATUS "Configuring LlamaCPP backend for Windows")
target_compile_definitions(rac_backend_llamacpp PRIVATE NOMINMAX WIN32_LEAN_AND_MEAN)
target_link_libraries(rac_backend_llamacpp PUBLIC ws2_32)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check whether any public header of rac_backend_llamacpp references winsock APIs/types,
# which would justify PUBLIC linkage of ws2_32.
fd -e h -e hpp . sdk/runanywhere-commons/src/backends/llamacpp | xargs rg -n -C2 -P '\b(winsock2\.h|ws2tcpip\.h|WSAStartup|SOCKET|sockaddr_in|htons|htonl|ntohs|ntohl)\b' || echo "No winsock references found in llamacpp backend headers."

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 131


🏁 Script executed:

# 1. Find the public headers for rac_backend_llamacpp
find sdk/runanywhere-commons/include/rac -name "*llamacpp*" -o -name "*backend*" | head -20

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 314


🏁 Script executed:

# 2. Check the CMakeLists.txt file for context around line 287
sed -n '280,295p' sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 666


🏁 Script executed:

# 3. Search for actual ws2_32 usage in the llamacpp backend source
rg -n 'ws2_32|winsock|WSA' sdk/runanywhere-commons/src/backends/llamacpp --type cpp --type cc --type h

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 101


🏁 Script executed:

# 4. Check if there are any public header files for llamacpp backend
find sdk/runanywhere-commons/include -name "*.h" -o -name "*.hpp" | xargs rg -l "llamacpp" 2>/dev/null || echo "No llamacpp references in public headers"

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 339


🏁 Script executed:

# Check the public headers for any winsock/network-related symbols
cat -n sdk/runanywhere-commons/include/rac/backends/rac_llm_llamacpp.h

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 14244


🏁 Script executed:

cat -n sdk/runanywhere-commons/include/rac/backends/rac_vlm_llamacpp.h

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 8903


🏁 Script executed:

# Try searching for ws2_32 usage in source files with correct type
rg -n 'ws2_32|winsock|WSA' sdk/runanywhere-commons/src/backends/llamacpp

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 201


🏁 Script executed:

# Check if the backend actually needs ws2_32 by looking at implementation files
find sdk/runanywhere-commons/src/backends/llamacpp -type f \( -name "*.cpp" -o -name "*.h" -o -name "*.hpp" \) | head -20

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 583


🏁 Script executed:

# Search for actual winsock usage in implementation files
rg -n 'winsock|WSA|ws2_32|SOCKET|sockaddr|htons|ntohs' sdk/runanywhere-commons/src/backends/llamacpp --type-add 'cpp:*.{cpp,h,hpp}' -t cpp

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 56


🏁 Script executed:

# Also check if llamacpp library itself might need it (check external dependencies)
grep -r "ws2_32\|winsock\|WSA" sdk/runanywhere-commons/src/backends/llamacpp/ 2>/dev/null | head -20

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 197


Change ws2_32 linking to PRIVATE.

No winsock APIs or types are exposed in the public headers (rac_llm_llamacpp.h, rac_vlm_llamacpp.h), and no winsock usage was found in the backend implementation. Since ws2_32 is a pure implementation detail, linking it as PUBLIC unnecessarily propagates the Windows-only dependency to all transitive consumers. Change to PRIVATE to avoid leaking this platform-specific requirement.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt` at line 287,
Change the CMake target_link_libraries usage so the Windows sockets lib is not
propagated: in the CMakeLists where target_link_libraries(rac_backend_llamacpp
PUBLIC ws2_32) is used, change the link scope from PUBLIC to PRIVATE (i.e., make
ws2_32 a private dependency of the rac_backend_llamacpp target) so the
Windows-only ws2_32 symbol is not exposed to transitive consumers.

Comment on lines +33 to +46
get_filename_component(PACKAGE_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/.." ABSOLUTE)
get_filename_component(REPO_ROOT_FROM_APP "${CMAKE_SOURCE_DIR}/../../../../" ABSOLUTE)
set(ONNX_DIST_DIR_CANDIDATES
"${REPO_ROOT_FROM_APP}/sdk/runanywhere-commons/dist/windows/x64"
"${PACKAGE_ROOT}/../../../runanywhere-commons/dist/windows/x64"
)

set(ONNX_DIST_DIR "")
foreach(candidate ${ONNX_DIST_DIR_CANDIDATES})
if(EXISTS "${candidate}")
set(ONNX_DIST_DIR "${candidate}")
break()
endif()
endforeach()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fallback path only works for the bundled example app.

REPO_ROOT_FROM_APP assumes the consumer sits exactly four levels deep under the repo root (matches examples/flutter/RunAnywhereAI/windows/). The PACKAGE_ROOT/../../.. candidate similarly assumes the package lives at sdk/runanywhere-flutter/packages/runanywhere_onnx on disk — which won't hold when the plugin is consumed from pub cache or a symlinked checkout elsewhere. For third-party apps this silently falls through to the "ONNX dist directory not found" warning and ships the app with no ONNX runtime.

Consider documenting an override like -DRAC_ONNX_DIST_DIR=<path> and honoring it as the first candidate so external integrators have a reliable override.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/CMakeLists.txt`
around lines 33 - 46, Add a configurable override for the ONNX distribution path
and check it before the hardcoded candidates: declare and use a CMake cache
variable (e.g. RAC_ONNX_DIST_DIR) and, if set and EXISTS, set ONNX_DIST_DIR to
that value before iterating ONNX_DIST_DIR_CANDIDATES; keep existing PACKAGE_ROOT
and REPO_ROOT_FROM_APP candidates as fallbacks and preserve the final "not
found" warning behavior so external consumers (pub cache or symlinked checkouts)
can reliably pass -DRAC_ONNX_DIST_DIR=<path>.

Comment on lines +48 to +63
if(EXISTS "${ONNX_DIST_DIR}")
file(GLOB ONNX_DIST_DLLS "${ONNX_DIST_DIR}/*.dll")
set(ONNX_RUNTIME_DLLS "")

foreach(candidate ${ONNX_DIST_DLLS})
get_filename_component(candidate_name "${candidate}" NAME)
if(candidate_name STREQUAL "rac_backend_onnx.dll")
list(APPEND ONNX_RUNTIME_DLLS "${candidate}")
elseif(candidate_name MATCHES "^onnxruntime.*\\.dll$")
list(APPEND ONNX_RUNTIME_DLLS "${candidate}")
elseif(candidate_name MATCHES "^sherpa-onnx.*\\.dll$")
list(APPEND ONNX_RUNTIME_DLLS "${candidate}")
elseif(candidate_name STREQUAL "cargs.dll")
list(APPEND ONNX_RUNTIME_DLLS "${candidate}")
endif()
endforeach()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Locate any Windows CMakeLists for the core runanywhere plugin and check for rac_commons.dll bundling.
fd -t f CMakeLists.txt sdk/runanywhere-flutter/packages/runanywhere/windows
rg -nP --type=cmake -C3 '\brac_commons(\.dll)?\b'
# Check the example app's Windows tree for explicit copies.
rg -nP -C3 '\brac_commons\.dll\b' examples/flutter/RunAnywhereAI/windows

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 40523


🏁 Script executed:

# Check the ONNX plugin's pubspec.yaml for dependencies
fd -t f pubspec.yaml sdk/runanywhere-flutter/packages/runanywhere_onnx | head -1 | xargs cat

# Also check the core plugin's full Windows CMakeLists to see how bundled_libraries is exported
head -80 sdk/runanywhere-flutter/packages/runanywhere/windows/CMakeLists.txt

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 3041


🏁 Script executed:

# Get full core plugin Windows CMakeLists
cat -n sdk/runanywhere-flutter/packages/runanywhere/windows/CMakeLists.txt

# Get full ONNX plugin Windows CMakeLists  
cat -n sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/CMakeLists.txt

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 5897


rac_commons.dll dependency not explicitly bundled by ONNX plugin; relies on transitive propagation from core plugin.

The review raised concerns about the DLL allowlist and missing-dist handling. Verification shows:

  1. Missing-dist error handling is less defensive here than in the core plugin. The core runanywhere plugin (which ONNX depends on per pubspec.yaml) uses a FATAL_ERROR to fail the build if rac_commons.dll is missing. The ONNX plugin instead uses a WARNING and silently continues, making it easier to accidentally ship builds without ONNX support.

  2. rac_commons.dll is not in the ONNX allowlist. The allowlist filters for rac_backend_onnx.dll, onnxruntime*.dll, sherpa-onnx*.dll, and cargs.dll only. The core plugin bundles rac_commons.dll separately, so the ONNX plugin implicitly relies on Flutter's plugin dependency system to propagate it. This is fragile—if the app omits the core plugin (unlikely, but possible) or if the dependency propagation fails, the runtime will fail with a missing-library error when loading rac_backend_onnx.dll.

Recommendation: Either explicitly include rac_commons.dll in the allowlist as a defensive measure, or add a comment documenting that this plugin depends on the core runanywhere plugin to provide rac_commons.dll at runtime. Also consider using FATAL_ERROR instead of WARNING when the dist directory is missing, to match the core plugin's safer behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/CMakeLists.txt`
around lines 48 - 63, The ONNX plugin's DLL allowlist and missing-dist handling
are fragile: add "rac_commons.dll" to the allowlist so it is bundled explicitly
(update the logic that builds ONNX_RUNTIME_DLLS to include candidate_name
STREQUAL "rac_commons.dll"), and make the missing distribution error behavior
match the core plugin by treating a missing ONNX_DIST_DIR as a fatal build error
(use FATAL_ERROR instead of WARNING) or at minimum add a clear comment in the
CMakeLists noting the runtime dependency on the core runanywhere plugin
providing rac_commons.dll; adjust the code paths that reference ONNX_DIST_DIR,
ONNX_DIST_DLLS, and ONNX_RUNTIME_DLLS accordingly.

Comment on lines +64 to +77
test('copies UTF-8 bytes into secure storage callback buffer', () {
final buffer = calloc<Uint8>(16).cast<Utf8>();

try {
final written = copyUtf8StringToBufferForTesting('你好', buffer, 16);
final bytes = buffer.cast<Uint8>().asTypedList(written);

expect(written, utf8.encode('你好').length);
expect(bytes, utf8.encode('你好'));
expect(buffer.cast<Uint8>()[written], 0);
} finally {
calloc.free(buffer);
}
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Null-terminator assertion is masked by calloc zero-init.

Because calloc zero-fills the 16-byte buffer, buffer.cast<Uint8>()[written] == 0 would pass even if _copyUtf8StringToBuffer failed to write the terminator. Pre-fill with a non-zero sentinel so the check actually exercises the write.

🧪 Suggested tightening
   test('copies UTF-8 bytes into secure storage callback buffer', () {
     final buffer = calloc<Uint8>(16).cast<Utf8>();

     try {
+      // Poison the buffer so a missing null terminator would surface.
+      final raw = buffer.cast<Uint8>();
+      for (var i = 0; i < 16; i++) {
+        raw[i] = 0xAA;
+      }
       final written = copyUtf8StringToBufferForTesting('你好', buffer, 16);
       final bytes = buffer.cast<Uint8>().asTypedList(written);

       expect(written, utf8.encode('你好').length);
       expect(bytes, utf8.encode('你好'));
       expect(buffer.cast<Uint8>()[written], 0);
     } finally {
       calloc.free(buffer);
     }
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-flutter/packages/runanywhere/test/dart_bridge_auth_test.dart`
around lines 64 - 77, The test's null-terminator assertion is masked because
calloc zero-initializes the buffer; before calling
copyUtf8StringToBufferForTesting, overwrite the allocated buffer (buffer /
buffer.cast<Uint8>()) with a non-zero sentinel (e.g., 0xFF) so the function must
explicitly write the terminator, then call
copyUtf8StringToBufferForTesting('你好', buffer, 16) and assert the byte at index
written is 0; keep using calloc.free(buffer) in the finally block.

@peilinok peilinok closed this Apr 27, 2026
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.

2 participants