Skip to content

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

Closed
peilinok wants to merge 24 commits intoRunanywhereAI:mainfrom
peilinok:sylar/win
Closed

feat: support to build for windows and add proxy when do http request…#481
peilinok wants to merge 24 commits intoRunanywhereAI:mainfrom
peilinok:sylar/win

Conversation

@peilinok
Copy link
Copy Markdown

@peilinok peilinok commented Apr 15, 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 desktop support for the RunAnywhereAI example (native Windows runner and plugins)
    • Vision (camera) and voice features restored on Windows; Windows camera support added
    • Network Proxy UI with separate General and Download proxy configs (configure/test/clear)
    • Configurable HTTP client factory for model downloads
  • Documentation

    • Windows build and validation guides for voice and vision support
  • Bug Fixes

    • Improved model file discovery to prefer primary model files and skip auxiliary artifacts
    • More resilient Windows dependency downloads and extraction checks

Copilot AI review requested due to automatic review settings April 15, 2026 12:16
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 15, 2026

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

@peilinok
Copy link
Copy Markdown
Author

peilinok commented Apr 15, 2026

chat in flutter example
image

http proxy
image

voice assistant
image

vision
image

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

Adds initial Windows (preview) support for the Flutter SDK + example app, and introduces configurable proxy support for HTTP requests—especially model downloads—along with some Windows-specific native changes in runanywhere-commons.

Changes:

  • Add Windows Flutter plugin implementations (C++ + CMake) for runanywhere, runanywhere_llamacpp, and runanywhere_onnx, plus Windows example runner scaffolding.
  • Add a configurable download-only HTTP client factory (enabling proxy usage for model downloads) and wire it from the Flutter example app.
  • Update runanywhere-commons for Windows vertical-slice behavior (filesystem traversal, extraction stub, Windows build script/docs).

Reviewed changes

Copilot reviewed 68 out of 104 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 Windows C-API registrar entrypoint for ONNX plugin
sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin.h Windows ONNX plugin header
sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin.cpp Windows ONNX method-channel implementation
sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/include/runanywhere_onnx/runanywhere_onnx_plugin_c_api.h Exported C-API header for ONNX plugin
sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/CMakeLists.txt Build + bundle ONNX backend DLL for Windows plugin
sdk/runanywhere-flutter/packages/runanywhere_onnx/pubspec.yaml Declare Windows plugin for runanywhere_onnx
sdk/runanywhere-flutter/packages/runanywhere_onnx/lib/native/onnx_bindings.dart Load ONNX backend DLL on Windows
sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin_c_api.cpp Windows C-API registrar entrypoint for LlamaCpp plugin
sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.h Windows LlamaCpp plugin header
sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.cpp Windows LlamaCpp method-channel implementation
sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/include/runanywhere_llamacpp/runanywhere_llamacpp_plugin_c_api.h Exported C-API header for LlamaCpp plugin
sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/CMakeLists.txt Build + bundle LlamaCpp backend DLL for Windows plugin
sdk/runanywhere-flutter/packages/runanywhere_llamacpp/pubspec.yaml Declare Windows plugin for runanywhere_llamacpp
sdk/runanywhere-flutter/packages/runanywhere_llamacpp/lib/native/llamacpp_bindings.dart Load LlamaCpp backend DLL on Windows
sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin_c_api.cpp Windows C-API registrar entrypoint for core plugin
sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin.h Windows core plugin header
sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin.cpp Windows core method-channel implementation
sdk/runanywhere-flutter/packages/runanywhere/windows/include/runanywhere/runanywhere_plugin_c_api.h Exported C-API header for core plugin
sdk/runanywhere-flutter/packages/runanywhere/windows/CMakeLists.txt Build + bundle rac_commons.dll for Windows core plugin
sdk/runanywhere-flutter/packages/runanywhere/pubspec.yaml Declare Windows plugin for runanywhere
sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart Add download HTTP client factory configuration API
sdk/runanywhere-flutter/packages/runanywhere/lib/native/platform_loader.dart Improve Windows DLL search path (include executable dir)
sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart Add injectable HTTP client factory for downloads
sdk/runanywhere-flutter/packages/runanywhere/README.md Document Windows setup + support matrix update
sdk/runanywhere-commons/src/infrastructure/extraction/rac_extraction_stub.cpp Add extraction stub for builds without native extraction
sdk/runanywhere-commons/src/infrastructure/download/download_orchestrator.cpp Windows filesystem-based directory traversal helpers
sdk/runanywhere-commons/src/core/rac_core.cpp Restrict diffusion registry init/cleanup to Apple platforms
sdk/runanywhere-commons/src/backends/onnx/CMakeLists.txt Add Windows build-path status message for ONNX backend
sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt Add Windows backend config and build flags for LlamaCpp
sdk/runanywhere-commons/scripts/build-windows.bat Simplified Windows build script producing DLL artifacts
sdk/runanywhere-commons/README.md Document Windows build steps + current limitations
examples/flutter/RunAnywhereAI/windows/runner/win32_window.h Windows runner window abstraction (Flutter template)
examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp Windows runner window implementation (Flutter template)
examples/flutter/RunAnywhereAI/windows/runner/utils.h Windows runner utilities (Flutter template)
examples/flutter/RunAnywhereAI/windows/runner/utils.cpp Windows runner utilities implementation (Flutter template)
examples/flutter/RunAnywhereAI/windows/runner/runner.exe.manifest Runner manifest (DPI awareness/compat)
examples/flutter/RunAnywhereAI/windows/runner/resource.h Windows runner resource header
examples/flutter/RunAnywhereAI/windows/runner/main.cpp Windows runner entrypoint
examples/flutter/RunAnywhereAI/windows/runner/flutter_window.h Runner FlutterWindow wrapper
examples/flutter/RunAnywhereAI/windows/runner/flutter_window.cpp Runner FlutterWindow implementation
examples/flutter/RunAnywhereAI/windows/runner/Runner.rc Runner resources/version metadata
examples/flutter/RunAnywhereAI/windows/runner/CMakeLists.txt Runner target build configuration
examples/flutter/RunAnywhereAI/windows/flutter/generated_plugins.cmake Windows generated plugin list for example app
examples/flutter/RunAnywhereAI/windows/flutter/generated_plugin_registrant.h Windows generated plugin registrant header
examples/flutter/RunAnywhereAI/windows/flutter/generated_plugin_registrant.cc Windows generated plugin registrant implementation
examples/flutter/RunAnywhereAI/windows/flutter/CMakeLists.txt Flutter Windows toolchain wrapper (template)
examples/flutter/RunAnywhereAI/windows/CMakeLists.txt Example app Windows top-level CMake
examples/flutter/RunAnywhereAI/windows/.gitignore Windows build artifacts ignores
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/include/flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h Vendored plugin Windows C-API header (include path)
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/flutter_secure_storage_windows_plugin.h Vendored plugin Windows C-API header (local)
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/flutter_secure_storage_windows_plugin.cpp Vendored plugin Windows implementation
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/CMakeLists.txt Build config for vendored Windows secure storage plugin
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/pubspec.yaml Vendored plugin pubspec
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_stub.dart Non-FFI stub implementation
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart FFI-backed Windows implementation
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/flutter_secure_storage_windows.dart Conditional export (stub vs FFI)
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/win32_window.h Vendored example runner file
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/win32_window.cpp Vendored example runner file
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/utils.h Vendored example runner file
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/utils.cpp Vendored example runner file
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/runner.exe.manifest Vendored example runner file
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/resource.h Vendored example runner file
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/main.cpp Vendored example runner file
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/flutter_window.h Vendored example runner file
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/flutter_window.cpp Vendored example runner file
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/Runner.rc Vendored example runner file
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/CMakeLists.txt Vendored example runner build config
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/flutter/generated_plugins.cmake Vendored generated CMake plugin list
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/flutter/generated_plugin_registrant.h Vendored generated plugin registrant
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/flutter/generated_plugin_registrant.cc Vendored generated plugin registrant
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/flutter/CMakeLists.txt Vendored flutter toolchain wrapper
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/CMakeLists.txt Vendored example top-level CMake
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/pubspec.yaml Vendored example pubspec
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/lib/main.dart Vendored example app Dart code
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/analysis_options.yaml Vendored example analyzer config
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/README.md Vendored 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
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/CHANGELOG.md Vendored plugin changelog
examples/flutter/RunAnywhereAI/pubspec.yaml Pin record version + override secure storage windows
examples/flutter/RunAnywhereAI/lib/features/voice/voice_assistant_view.dart Gate unsupported voice assistant on Windows
examples/flutter/RunAnywhereAI/lib/features/voice/text_to_speech_view.dart Gate unsupported TTS on Windows
examples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dart Gate unsupported STT on Windows
examples/flutter/RunAnywhereAI/lib/features/vision/vision_hub_view.dart Gate unsupported vision on Windows
examples/flutter/RunAnywhereAI/lib/features/settings/tool_settings_view_model.dart Route sample HTTP calls through proxy-aware service
examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart Add UI for general/download proxy configuration
examples/flutter/RunAnywhereAI/lib/features/rag/rag_demo_view.dart Gate unsupported RAG on Windows
examples/flutter/RunAnywhereAI/lib/features/chat/chat_interface_view.dart Prevent navigation to RAG when unsupported
examples/flutter/RunAnywhereAI/lib/core/utilities/constants.dart Add proxy-related key/value constants
examples/flutter/RunAnywhereAI/lib/core/services/proxy_settings_service.dart Persist/validate proxy settings (prefs + keychain)
examples/flutter/RunAnywhereAI/lib/core/services/platform_capability_service.dart Centralize Windows feature gating flags
examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart Create proxy-aware HttpClient/IOClient helpers
examples/flutter/RunAnywhereAI/lib/core/models/proxy_settings.dart Proxy settings models/enums
examples/flutter/RunAnywhereAI/lib/core/design_system/unsupported_feature_view.dart Reusable unsupported-feature UI
examples/flutter/RunAnywhereAI/lib/core/design_system/app_colors.dart Adjust grouped background color + add compat extension
examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart Wire download proxy factory + update module logs
examples/flutter/RunAnywhereAI/ios/Runner/GeneratedPluginRegistrant.m Update plugin registration headers/names
examples/flutter/RunAnywhereAI/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java Update plugin registration error message
examples/flutter/RunAnywhereAI/.gitignore Stop ignoring windows/ so Windows project is tracked

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

Comment on lines 26 to 30
import 'package:runanywhere/native/dart_bridge_rag.dart';
import 'package:runanywhere/native/dart_bridge_structured_output.dart';
import 'package:runanywhere/native/dart_bridge_vlm.dart';
import 'package:runanywhere/native/ffi_types.dart' show RacVlmImageFormat;
import 'package:runanywhere/native/dart_bridge_structured_output.dart';
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

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

There are two identical imports of dart_bridge_structured_output.dart in this import block, which will cause analyzer/linter failures (and may fail compilation depending on settings). Remove the duplicate and keep a single import (and ensure it’s placed consistently with the surrounding import ordering).

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

Choose a reason for hiding this comment

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

@copilot apply changes based on this feedback

Comment on lines +23 to +26
for %%I in ("%~dp0.") do set "SCRIPT_HOME=%%~fI"
set "ROOT=%SCRIPT_HOME%"
set "BUILD_DIR=%ROOT%\build-windows-x64"
set "DIST_DIR=%ROOT%\dist\windows\x64"
Copy link

Copilot AI Apr 15, 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 scripts directory (%~dp0), but the subsequent cmake -S "%ROOT%" expects the source root containing CMakeLists.txt (likely sdk/runanywhere-commons). As written, this will configure CMake from sdk/runanywhere-commons/scripts, which doesn’t contain a top-level CMakeLists.txt, so the build will fail. Update ROOT to point at the repo/package root (e.g., %SCRIPT_HOME%\..) and keep BUILD_DIR/DIST_DIR under that root.

Copilot uses AI. Check for mistakes.
Comment on lines +184 to +187
final file = File(destPath);
await file.create(recursive: true);
final sink = file.openWrite();
var downloaded = 0;
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

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

In the multi-file download path, the file sink is created with file.openWrite() but isn’t protected by a try/finally. If an exception occurs while streaming (network error, disk full, cancellation, etc.), the sink may remain open and the partial file may be left in a corrupted state. Wrap the sink usage in a try/finally (closing in finally) and consider cleaning up the partially written file on failure.

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

Choose a reason for hiding this comment

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

@copilot apply changes based on this feedback

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 16, 2026

📝 Walkthrough

Walkthrough

Adds extensive Windows support across the example app and SDK: Windows build tooling/CMake, native Windows plugins (RunAnywhere, LlamaCPP, ONNX), a Windows flutter_secure_storage implementation with tests, proxy/HTTP client configuration, platform capability gating in the UI, VLM/vision refactors and helpers, and multiple Windows-specific build/download scripts and utilities.

Changes

Cohort / File(s) Summary
Git config
/.gitignore, examples/flutter/RunAnywhereAI/.gitignore, examples/flutter/RunAnywhereAI/windows/.gitignore
Added .worktrees/ ignore; removed windows/ from example ignore; added Windows/Visual Studio ignore rules.
Docs & design
docs/building.md, docs/superpowers/specs/2026-04-16-windows-onnx-voice-restoration-design.md, docs/superpowers/specs/2026-04-17-windows-vision-design.md, docs/superpowers/plans/2026-04-17-windows-vision-restoration.md
New Windows build/validation docs and design specs for ONNX voice and Vision restoration.
Example app init & deps
examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart, examples/flutter/RunAnywhereAI/pubspec.yaml, examples/flutter/RunAnywhereAI/android/.../GeneratedPluginRegistrant.java, examples/flutter/RunAnywhereAI/ios/Runner/GeneratedPluginRegistrant.m
Configured download HTTP client factory, adjusted imports, added camera_windows, downgraded record, updated generated plugin registrants and dependency overrides.
Design system & widget
examples/flutter/RunAnywhereAI/lib/core/design_system/app_colors.dart, .../unsupported_feature_view.dart
Added ColorCompatibility.withValues(...) extension and UnsupportedFeatureView widget.
Proxy & HTTP stack
examples/flutter/RunAnywhereAI/lib/core/models/proxy_settings.dart, .../proxy_settings_service.dart, .../example_http_service.dart, .../utilities/constants.dart
New proxy models, validation/persistence service, scoped HTTP client creation with proxy (HTTP/HTTPS/SOCKS5), test helpers, and secure-key constants.
Platform capability gating
multiple feature files (.../platform_capability_service.dart, voice/vision/rag/chat UI files, model selection)
Added PlatformCapabilityService and runtime guards rendering UnsupportedFeatureView; adjusted model-loading flows for Windows gating.
Settings UI & tooling
examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart, .../tool_settings_view_model.dart, .../model_list_view_model.dart
Full proxy UI (configure/test/clear per scope), migrate HTTP calls to ExampleHttpService, improved error handling and async fixes.
Windows Flutter runner & CMake
examples/flutter/RunAnywhereAI/windows/*, examples/flutter/RunAnywhereAI/windows/flutter/*, .../windows/runner/*
Complete Windows CMake build, generated plugin registration, runner executable, high-DPI Win32 window, utilities, resources, manifest, and native asset bundling.
flutter_secure_storage Windows vendor
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/*
Added Windows plugin (FFI + stub), DPAPI-encrypted JSON storage, C++ plugin with AES-GCM/CNG and Credential Manager fallback, integration/example, tests, and packaging files.
SDK Windows build & scripts
sdk/runanywhere-commons/scripts/build-windows.bat, .../scripts/windows/download-sherpa-onnx.bat
Reworked Windows build script (backend selection, artifact staging) and improved Sherpa-ONNX download/extract/verification with retries and layout normalization.
Native SDK Windows support
sdk/runanywhere-commons/src/...
Windows-specific CMake branches, ONNX embedding flag, filesystem-based directory probing, mmproj file filtering, extraction stub, diffusion registry platform gating, and related tests.
Flutter SDK Windows plugins & bridges
sdk/runanywhere-flutter/packages/runanywhere*/windows/*, .../lib/public/runanywhere.dart, .../download/download_service.dart
Added Windows plugin targets (runanywhere, llamacpp, onnx), C API registration bridges, configurable download HTTP client factory, improved platform loader search, and isolate argument capture fixes.
VLM / vision file-resolution & tests
sdk/runanywhere-flutter/packages/runanywhere/lib/internal/vlm_file_resolution.dart, related tests, vision service/VM refactors, and view-model/widget tests
New VLM file-resolution utilities that exclude mmproj auxiliary files, vision camera, permission, and VLM service abstractions with refactors and tests.

Sequence Diagram(s)

sequenceDiagram
    participant App as Flutter Example
    participant CapSvc as PlatformCapabilityService
    participant UI as Feature View
    participant Unsupported as UnsupportedFeatureView

    App->>CapSvc: query supportsX (e.g., supportsVision/supportsRag)
    CapSvc->>CapSvc: evaluate Platform.isWindows
    alt Feature unsupported on Windows
        CapSvc-->>App: false
        App->>UI: build()
        UI->>Unsupported: instantiate with title/message
        Unsupported-->>UI: rendered scaffold
        UI-->>App: return Unsupported Scaffold
    else Feature supported
        CapSvc-->>App: true
        App->>UI: build()
        UI-->>App: render full feature UI
    end
Loading
sequenceDiagram
    participant Settings as CombinedSettingsView
    participant ProxySvc as ProxySettingsService
    participant Keychain as KeychainHelper
    participant Prefs as SharedPreferences
    participant HTTP as ExampleHttpService

    Settings->>ProxySvc: load(scope)
    ProxySvc->>Prefs: read keys for scope
    ProxySvc->>Keychain: read username/password
    Keychain-->>ProxySvc: credentials
    ProxySvc-->>Settings: return ProxySettings
    Settings->>ProxySvc: save(scope, settings)
    ProxySvc->>ProxySvc: validate(settings)
    ProxySvc->>Prefs: persist enabled/scheme/host/port/bypass
    ProxySvc->>Keychain: store/delete credentials
    ProxySvc-->>Settings: validation result
    Settings->>HTTP: testProxy(scope, uri)
    HTTP->>ProxySvc: getCurrent(scope)
    ProxySvc-->>HTTP: returns settings
    HTTP->>HTTP: createScopedHttpClient(uri) (findProxy/auth config)
    HTTP-->>Settings: return success/failure
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested labels

enhancement, documentation

Suggested reviewers

  • shubhammalhotra28
  • sanchitmonga22
  • Siddhesh2377

Poem

🥕 I hopped in with CMake, a tiny build hat,
DLLs in my basket and tests—just like that.
Proxies and cameras and DPAPI delight,
Windows now sings in the RunAnywhere night. ✨

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

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

Note

Due to the large number of review comments, Critical 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 (2)
sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt (1)

285-298: ⚠️ Potential issue | 🔴 Critical

Duplicate elseif(RAC_PLATFORM_WINDOWS) in platform-specific configuration section.

Similar to the issue above, lines 295-298 contain another duplicate Windows block that will never execute. The second block only has a comment about threading being built-in, while the first block (lines 285-288) actually links ws2_32.

Remove the duplicate block at lines 295-298.

🐛 Proposed fix: remove duplicate Windows block
 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
285 - 298, The CMake platform configuration contains a duplicate
elseif(RAC_PLATFORM_WINDOWS) block that will never execute; remove the redundant
second elseif(RAC_PLATFORM_WINDOWS) block (the one with only the comment about
threading) so the platform branches become elseif(RAC_PLATFORM_WINDOWS) {
message + target_compile_definitions + target_link_libraries(ws2_32) }
elseif(RAC_PLATFORM_LINUX) { target_link_libraries(pthread dl) } endif(),
ensuring only one RAC_PLATFORM_WINDOWS branch remains for
target_link_libraries(rac_backend_llamacpp) and target_compile_definitions.
sdk/runanywhere-commons/src/backends/onnx/CMakeLists.txt (1)

325-334: ⚠️ Potential issue | 🟡 Minor

Duplicate elseif(RAC_PLATFORM_WINDOWS) creates dead code.

There are two elseif(RAC_PLATFORM_WINDOWS) blocks in the platform configuration chain. The second one at lines 331-334 is unreachable because the first one at lines 325-327 will always match first. Remove the duplicate block.

🔧 Proposed fix
 elseif(RAC_PLATFORM_WINDOWS)
     message(STATUS "ONNX Backend: Windows build path available when ONNX dependencies are configured")
+    # No extra link libraries needed on Windows
 
 elseif(RAC_PLATFORM_LINUX)
     target_link_libraries(rac_backend_onnx PUBLIC pthread dl)
-
-elseif(RAC_PLATFORM_WINDOWS)
-    # No extra link libraries needed on Windows
-    message(STATUS "ONNX Backend: CPU EP for Windows")
 endif()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-commons/src/backends/onnx/CMakeLists.txt` around lines 325 -
334, There is a duplicate elseif(RAC_PLATFORM_WINDOWS) block that is
unreachable; remove the second elseif(RAC_PLATFORM_WINDOWS) block (the one
printing "ONNX Backend: CPU EP for Windows") so the platform chain is not
duplicated, leaving only the first elseif(RAC_PLATFORM_WINDOWS) message and the
RAC_PLATFORM_LINUX target_link_libraries(rac_backend_onnx PUBLIC pthread dl)
branch intact; if Windows-specific linking or messages are required, consolidate
them into the existing first elseif(RAC_PLATFORM_WINDOWS) block instead of
adding a second block.
🟠 Major comments (17)
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/LICENSE-1-29 (1)

1-29: ⚠️ Potential issue | 🟠 Major

Remove the vendored flutter_secure_storage_windows package entirely.

The main pubspec.yaml already declares flutter_secure_storage: ^9.0.0 as a dependency. flutter_secure_storage_windows is an endorsed implementation that gets automatically included when the parent package is declared—vendoring it is redundant and creates unnecessary maintenance burden. Additionally, the vendored version (3.1.2) is outdated compared to the latest on pub.dev (4.1.0), and the package itself recommends using flutter_secure_storage instead.

🤖 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/LICENSE`
around lines 1 - 29, Remove the vendored flutter_secure_storage_windows package
and its files (the LICENSE and all vendor folder contents referencing
flutter_secure_storage_windows v3.1.2); rely on the existing top-level
dependency flutter_secure_storage ^9.0.0 declared in pubspec.yaml so the
endorsed Windows implementation is resolved automatically—delete the vendor
directory and any references/imports to flutter_secure_storage_windows to avoid
redundancy and stale code.
examples/flutter/RunAnywhereAI/lib/core/services/platform_capability_service.dart-1-1 (1)

1-1: ⚠️ Potential issue | 🟠 Major

Remove dart:io from shared Flutter UI code.

The import 'dart:io' on line 1 breaks web compilation. Platform.isWindows (lines 12, 16) is unavailable for Flutter web builds. Use kIsWeb and defaultTargetPlatform from package:flutter/foundation.dart instead.

Suggested fix
-import 'dart:io';
+import 'package:flutter/foundation.dart';

 class PlatformCapabilityService {
   static const PlatformCapabilityService shared = PlatformCapabilityService._();

   const PlatformCapabilityService._();

+  bool get _isWindows =>
+      !kIsWeb && defaultTargetPlatform == TargetPlatform.windows;
+
   bool get supportsChat => true;
   bool get supportsTools => true;
   bool get supportsStructuredOutput => true;

-  bool get supportsVision => !Platform.isWindows;
+  bool get supportsVision => !_isWindows;
   bool get supportsSpeechToText => true;
   bool get supportsTextToSpeech => true;
   bool get supportsVoiceAssistant => true;
-  bool get supportsRag => !Platform.isWindows;
+  bool get supportsRag => !_isWindows;
🤖 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`
at line 1, Remove the `import 'dart:io'` and replace any use of
`Platform.isWindows` with a web-safe check using `kIsWeb` and
`defaultTargetPlatform` from `package:flutter/foundation.dart`; specifically
import `package:flutter/foundation.dart` and change checks like
`Platform.isWindows` to `!kIsWeb && defaultTargetPlatform ==
TargetPlatform.windows` (or use `kIsWeb` early to short-circuit), ensuring
platform-specific branches in platform_capability_service.dart reference
`kIsWeb`, `defaultTargetPlatform`, and `TargetPlatform` instead of `Platform`.
sdk/runanywhere-flutter/packages/runanywhere/README.md-86-103 (1)

86-103: ⚠️ Potential issue | 🟠 Major

Clarify repo-relative vs consumer setup commands.

Line 91 and Line 98 assume a monorepo checkout (sdk\..., examples\...). For pub package consumers, these paths won’t exist, so setup is not actionable.

🛠️ Suggested doc split
 ## Windows Setup (Preview)

-Build native DLLs first:
+If you are working inside this monorepo, build native DLLs first:
 ```bat
 cd sdk\runanywhere-commons
 call scripts\build-windows.bat llamacpp --clean

-Then build the example app:
+Then build the bundled example app:

cd examples\flutter\RunAnywhereAI
fvm flutter build windows

+If you are integrating runanywhere into your own Flutter app from pub.dev,
+build your app directly from your project root:
+bat +flutter build windows +

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @sdk/runanywhere-flutter/packages/runanywhere/README.md around lines 86 -
103, The Windows Setup (Preview) docs assume a monorepo checkout and uses
repo-relative commands under the sdk and examples folders, which won’t work for
pub package consumers; update the "Windows Setup (Preview)" README section to
split instructions: 1) keep the existing monorepo-specific steps for building
native DLLs and the bundled example (referencing the existing build-windows.bat
invocation and the fvm flutter build windows step) and 2) add a clear
consumer-focused subsection telling pub.dev integrators to build from their app
root (e.g., "flutter build windows") and to skip the repo-specific build scripts
unless they vendor or copy the native DLLs, and label the example build steps as
"bundled example" so readers know which workflow applies to them.


</details>

</blockquote></details>
<details>
<summary>sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart-2280-2287 (1)</summary><blockquote>

`2280-2287`: _⚠️ Potential issue_ | _🟠 Major_

**Proxy support is still incomplete for the SDK's own HTTP calls.**

This new hook only configures `ModelDownloadService`. Device registration, auth, telemetry, and any other SDK API traffic in this file still go through other services, so a proxy-only environment can still fail before downloads even start.

<details>
<summary>🤖 Prompt for AI Agents</summary>

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 2280 - 2287, The current configureDownloadHttpClientFactory only
configures ModelDownloadService but leaves device registration, auth, telemetry
and other SDK API traffic using the default HTTP client; extend the method to
set the same factory for the SDK-wide HTTP layer so all SDK network calls honor
the proxy. Specifically, in configureDownloadHttpClientFactory (which currently
calls ModelDownloadService.shared.configureClientFactory), also invoke the
central/shared HTTP client configurator used by other services (e.g.,
SDKHttpClient.shared.configureClientFactory or
NetworkService.shared.configureClientFactory — whichever is the SDK-wide HTTP
client in this module) so DeviceRegistrationService, AuthService,
TelemetryService, etc. use the provided factory; ensure null is handled the same
way and update any references to the global client so all internal SDK API calls
route through the configured factory.


</details>

</blockquote></details>
<details>
<summary>examples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dart-123-138 (1)</summary><blockquote>

`123-138`: _⚠️ Potential issue_ | _🟠 Major_

**Refresh STT model state on first mount too.**

This helper only runs after the picker callback now. If an STT model was already loaded before opening this screen, `_hasModelSelected` stays false and the blocking overlay remains until the user reopens the sheet.  


<details>
<summary>Suggested fix</summary>

```diff
  `@override`
  void initState() {
    super.initState();
    unawaited(_checkMicrophonePermission());
+   if (sdk.RunAnywhere.isSDKInitialized) {
+     unawaited(_refreshModelState());
+   }
  }
🤖 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, The screen never refreshes the STT model on first mount
so _hasModelSelected remains false if a model was loaded earlier; call
_refreshModelState() during widget initialization (e.g., in initState or the
appropriate lifecycle method) so the current model is fetched as soon as the
view mounts, and ensure you set _hasModelSelected based on whether currentModel
is non-null inside _refreshModelState (or immediately after awaiting
sdk.RunAnywhere.currentSTTModel()) so the blocking overlay is removed when a
model already exists.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/pubspec.yaml-5-7 (1)

5-7: ⚠️ Potential issue | 🟠 Major

Widen the example's Dart upper bound to Dart 3+.

The vendored example is capped at <3.0.0, which excludes Dart 3.x. The parent package already allows <4.0.0, and the main RunAnywhereAI project requires Dart 3.0.0+, creating an incompatibility. The example will fail dependency resolution under current toolchains.

Suggested fix
 environment:
-  sdk: '>=2.12.0 <3.0.0'
+  sdk: '>=2.12.0 <4.0.0'
   flutter: ">=2.0.0"
🤖 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/pubspec.yaml`
around lines 5 - 7, Update the environment SDK constraint in the pubspec.yaml
environment block so the example allows Dart 3.x; specifically change the sdk
entry in the environment section (the "sdk" constraint) from '>=2.12.0 <3.0.0'
to a range that permits Dart 3 (for example '>=2.12.0 <4.0.0') so it matches the
parent package and the main project.
sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart-184-207 (1)

184-207: ⚠️ Potential issue | 🟠 Major

Ensure file sink is always closed on stream errors/cancellation.

If response.stream throws, sink.flush/close on Lines 205-206 are skipped, leaving the file handle open.

✅ Proposed fix
-              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 file = File(destPath);
+              await file.create(recursive: true);
+              final sink = file.openWrite();
+              var downloaded = 0;
+              try {
+                await for (final chunk in response.stream) {
+                  sink.add(chunk);
+                  downloaded += chunk.length;
 
-                // Report progress proportionally across all files
-                final fileProgress = downloaded.toDouble() / (model.downloadSize ?? 1);
-                final overallProgress = (i + fileProgress) / totalFiles;
-                yield ModelDownloadProgress(
-                  modelId: modelId,
-                  bytesDownloaded: downloaded,
-                  totalBytes: model.downloadSize ?? 0,
-                  stage: ModelDownloadStage.downloading,
-                  overallProgress: overallProgress * 0.9,
-                );
-              }
-
-              await sink.flush();
-              await sink.close();
+                  // Report progress proportionally across all files
+                  final fileProgress =
+                      downloaded.toDouble() / (model.downloadSize ?? 1);
+                  final overallProgress = (i + fileProgress) / totalFiles;
+                  yield ModelDownloadProgress(
+                    modelId: modelId,
+                    bytesDownloaded: downloaded,
+                    totalBytes: model.downloadSize ?? 0,
+                    stage: ModelDownloadStage.downloading,
+                    overallProgress: overallProgress * 0.9,
+                  );
+                }
+                await sink.flush();
+              } finally {
+                await sink.close();
+              }
               _logger.info('Downloaded: ${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 184 - 207, The file sink opened for writing (sink =
file.openWrite()) can remain unclosed if response.stream throws or iteration is
cancelled; wrap the stream consumption and progress-yielding block in a
try/finally (or try/catch/finally) so that await sink.flush() and await
sink.close() always run in the finally block; update the method that contains
the response.stream loop (the download routine that yields ModelDownloadProgress
for modelId/descriptor) to move the existing flush/close into finally and
optionally log the error before rethrowing to preserve behavior.
examples/flutter/RunAnywhereAI/windows/runner/main.cpp-18-32 (1)

18-32: ⚠️ Potential issue | 🟠 Major

Balance COM initialization across all exit paths.

::CoInitializeEx on line 18 is called without checking its return value, and the early return on line 31 skips ::CoUninitialize. This leaves COM initialization unbalanced if window.Create fails, leaking COM state. The same pattern applies to the exit path on lines 41–42.

Check the HRESULT from CoInitializeEx and call CoUninitialize conditionally on all exit paths (both the failure return and the success return).

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

In `@examples/flutter/RunAnywhereAI/windows/runner/main.cpp` around lines 18 - 32,
Check the HRESULT returned by ::CoInitializeEx and, if it succeeds (S_OK or
S_FALSE), ensure ::CoUninitialize is called on every exit path: when
FlutterWindow::Create(...) fails and on the normal successful exit; i.e., record
the result of ::CoInitializeEx at the top of main, only proceed if
initialization succeeded or handle failure appropriately, and call
::CoUninitialize before each early return (including the failure after
window.Create) and at the end of main so COM initialization is balanced across
all paths.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/main.cpp-18-32 (1)

18-32: ⚠️ Potential issue | 🟠 Major

Balance COM lifecycle: validate initialization and ensure cleanup on all paths.

Line 18 initializes COM without validating the return value, and the early return at line 31 (when window creation fails) skips the CoUninitialize at line 41, creating a resource leak.

Recommended fix
-  ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
+  const HRESULT com_hr =
+      ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
+  const bool com_initialized = SUCCEEDED(com_hr);

   flutter::DartProject project(L"data");

   std::vector<std::string> command_line_arguments =
       GetCommandLineArguments();

   project.set_dart_entrypoint_arguments(std::move(command_line_arguments));

   FlutterWindow window(project);
   Win32Window::Point origin(10, 10);
   Win32Window::Size size(1280, 720);
   if (!window.Create(L"flutter_secure_storage_windows_example", origin, size)) {
+    if (com_initialized) {
+      ::CoUninitialize();
+    }
     return EXIT_FAILURE;
   }
   window.SetQuitOnClose(true);

   ::MSG msg;
   while (::GetMessage(&msg, nullptr, 0, 0)) {
     ::TranslateMessage(&msg);
     ::DispatchMessage(&msg);
   }

-  ::CoUninitialize();
+  if (com_initialized) {
+    ::CoUninitialize();
+  }
   return EXIT_SUCCESS;
🤖 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/main.cpp`
around lines 18 - 32, Check the return value of CoInitializeEx (store HRESULT hr
= CoInitializeEx(...)) and only call CoUninitialize if initialization succeeded;
ensure all early exits (e.g., when FlutterWindow::Create returns false and you
currently return EXIT_FAILURE) call CoUninitialize before returning to avoid
leaking COM. Concretely: modify the code around CoInitializeEx/CoUninitialize
and window.Create so that you track success (hr) and on the Create failure path
call CoUninitialize() if SUCCEEDED(hr) (or use a small RAII/scope guard around
CoInitializeEx for automatic cleanup), and skip CoUninitialize if CoInitializeEx
failed.
sdk/runanywhere-commons/scripts/build-windows.bat-80-95 (1)

80-95: ⚠️ Potential issue | 🟠 Major

Fail the build when a requested backend DLL is missing.

If onnx or llamacpp is selected and that backend fails to emit its DLL, this script still reaches Build complete. That turns a build failure into a later packaging/runtime failure.

Proposed fix
-if exist "%BUILD_DIR%\src\backends\llamacpp\Release\rac_backend_llamacpp.dll" (
-  copy /y "%BUILD_DIR%\src\backends\llamacpp\Release\rac_backend_llamacpp.dll" "%DIST_DIR%\rac_backend_llamacpp.dll" >nul
-)
+if /I "%BUILD_LLAMA%"=="ON" (
+  if not exist "%BUILD_DIR%\src\backends\llamacpp\Release\rac_backend_llamacpp.dll" (
+    echo ERROR: rac_backend_llamacpp.dll was not produced.
+    exit /b 1
+  )
+  copy /y "%BUILD_DIR%\src\backends\llamacpp\Release\rac_backend_llamacpp.dll" "%DIST_DIR%\rac_backend_llamacpp.dll" >nul
+  if errorlevel 1 exit /b 1
+)
 
-if exist "%BUILD_DIR%\src\backends\onnx\Release\rac_backend_onnx.dll" (
-  copy /y "%BUILD_DIR%\src\backends\onnx\Release\rac_backend_onnx.dll" "%DIST_DIR%\rac_backend_onnx.dll" >nul
-)
+if /I "%BUILD_ONNX%"=="ON" (
+  if not exist "%BUILD_DIR%\src\backends\onnx\Release\rac_backend_onnx.dll" (
+    echo ERROR: rac_backend_onnx.dll was not produced.
+    exit /b 1
+  )
+  copy /y "%BUILD_DIR%\src\backends\onnx\Release\rac_backend_onnx.dll" "%DIST_DIR%\rac_backend_onnx.dll" >nul
+  if errorlevel 1 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 80 - 95, The
script currently copies rac_backend_llamacpp.dll and rac_backend_onnx.dll but
doesn't fail the build when a requested backend is missing; modify the logic so
that when a backend is requested (check BUILD_ONNX=="ON" and similarly for the
llamacpp selection variable if present) you verify the destination DLL exists
after the copy and if it does not echo a clear error (e.g., "ERROR: missing
rac_backend_onnx.dll") and exit /b 1 to fail the build; also, when
BUILD_ONNX=="ON", after the for loops that copy sherpa-onnx DLLs verify at least
one sherpa-onnx DLL was copied (e.g., check if any matching files exist in
%DIST_DIR%) and exit with an error if none were found.
sdk/runanywhere-commons/src/infrastructure/download/download_orchestrator.cpp-215-239 (1)

215-239: ⚠️ Potential issue | 🟠 Major

Align Windows model file discovery with POSIX ordering.

The Windows implementation immediately recurses or returns when it encounters a directory or model file, but the POSIX implementation first scans all regular files in the current directory before recursing into subdirectories. Since std::filesystem::directory_iterator has no guaranteed order, the Windows branch can select a nested model before examining all files at the archive root, causing inconsistent model selection between platforms.

Proposed fix
 `#if` defined(_WIN32)
     std::error_code ec;
+    std::string found_model;
+    std::vector<std::string> subdirs;
     for (const auto& entry : std::filesystem::directory_iterator(directory, ec)) {
         const auto name = entry.path().filename().string();
         if (name == "." || name == "..") continue;
         if (!name.empty() && name[0] == '.') continue;
 
         const auto full_path = entry.path().string();
         if (entry.is_regular_file(ec)) {
             const char* dot = strrchr(name.c_str(), '.');
             if (dot && is_model_extension(dot + 1)) {
                 if (is_auxiliary_model_file(name)) {
                     continue;
                 }
-                snprintf(out_path, path_size, "%s", full_path.c_str());
-                return true;
+                found_model = full_path;
+                break;
             }
         } else if (entry.is_directory(ec)) {
-            if (find_single_model_file(full_path.c_str(), depth + 1, max_depth, out_path,
-                                       path_size)) {
-                return true;
-            }
+            subdirs.push_back(full_path);
         }
     }
+    if (!found_model.empty()) {
+        snprintf(out_path, path_size, "%s", found_model.c_str());
+        return true;
+    }
+    for (const auto& subdir : subdirs) {
+        if (find_single_model_file(subdir.c_str(), depth + 1, max_depth, out_path, path_size)) {
+            return true;
+        }
+    }
     return false;
 `#else`
🤖 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 215 - 239, The Windows branch of find_single_model_file mirrors
POSIX behavior: instead of immediately recursing or returning when encountering
an entry, first iterate the directory and process all regular files (use
entry.is_regular_file(ec), check name/dot/skips, use is_model_extension and
is_auxiliary_model_file, set out_path and return true if found), and only after
that do a second pass (or separate loop) over entries that are directories
calling find_single_model_file recursively; keep using std::error_code ec and
preserve the same filename/dot-skipping logic and checks (is_regular_file,
is_directory, is_model_extension, is_auxiliary_model_file) so Windows will
examine all files in the current directory before recursing into subdirectories.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/flutter_window.cpp-59-63 (1)

59-63: ⚠️ Potential issue | 🟠 Major

Potential null pointer dereference in WM_FONTCHANGE handler.

flutter_controller_ may be null when WM_FONTCHANGE is received (e.g., during window destruction or before Flutter initialization completes). The handler at line 61 dereferences flutter_controller_ without a null check, unlike the block at lines 50-57 which properly guards access.

🐛 Proposed fix
   switch (message) {
     case WM_FONTCHANGE:
-      flutter_controller_->engine()->ReloadSystemFonts();
+      if (flutter_controller_) {
+        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 59 - 63, The WM_FONTCHANGE case may dereference a null
flutter_controller_, so update the switch handler to guard access: check that
flutter_controller_ is non-null and that flutter_controller_->engine() is
non-null before calling ReloadSystemFonts (same pattern used in the earlier
block that checks flutter_controller_). If either is null, skip calling
ReloadSystemFonts (break/return) to avoid a crash.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/integration_test/app_test.dart-20-39 (1)

20-39: ⚠️ Potential issue | 🟠 Major

These tests can touch files outside their sandbox.

cleanUpFiles removes every .secure file from the real parent directory, and the '/../a' case probes traversal against the real storage root. Running this suite on a workstation can delete or create sibling-app data instead of only test artifacts.

Also applies to: 1036-1040

🤖 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/integration_test/app_test.dart`
around lines 20 - 39, The cleanup helper cleanUpFiles is deleting .secure files
from the real application support parent directory and tests use '/../a' which
can traverse out of the sandbox; change the tests and cleanUpFiles to operate in
a dedicated temp/test-specific directory (e.g., create a unique subdirectory
under getTemporaryDirectory() or a uniquely-named subfolder inside
getApplicationSupportDirectory()) and only delete files inside that directory
(never walk directory.parent); update any test cases that currently use '/../a'
to construct paths confined to that test directory (or use a mocked filesystem)
so the code under test cannot touch sibling or real app data; ensure references
to cleanUpFiles, the test that constructs '/../a', and any file deletion logic
are updated accordingly.
examples/flutter/RunAnywhereAI/lib/core/models/proxy_settings.dart-52-69 (1)

52-69: ⚠️ Potential issue | 🟠 Major

Allow copyWith to clear port.

port ?? this.port makes copyWith(port: null) a no-op, so a user who clears the port field keeps the previous port and the proxy can stay erroneously configured.

Suggested fix
 class ProxySettings {
+  static const Object _unset = Object();
+
   final bool enabled;
   final ProxyScheme scheme;
   final String host;
   final int? port;
@@
   ProxySettings copyWith({
     bool? enabled,
     ProxyScheme? scheme,
     String? host,
-    int? port,
+    Object? port = _unset,
     String? username,
     String? password,
     bool? bypassLocal,
   }) {
     return ProxySettings(
       enabled: enabled ?? this.enabled,
       scheme: scheme ?? this.scheme,
       host: host ?? this.host,
-      port: port ?? this.port,
+      port: identical(port, _unset) ? this.port : port as int?,
       username: username ?? this.username,
       password: password ?? this.password,
       bypassLocal: bypassLocal ?? this.bypassLocal,
     );
   }
🤖 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 52 - 69, The copyWith currently treats port: port ?? this.port so calling
copyWith(port: null) cannot clear the port; update ProxySettings.copyWith to
accept an explicit clear flag (e.g. add parameter bool clearPort = false) and
set the returned port using port: clearPort ? null : (port ?? this.port) so
callers can clear the port by passing clearPort: true, while preserving existing
behavior for other fields; modify ProxySettings.copyWith signature and the
returned ProxySettings initializer accordingly.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart-148-150 (1)

148-150: ⚠️ Potential issue | 🟠 Major

Await the legacy delete before returning from write().

Right now write() can complete before the backward-compatibility cleanup does. Callers can then observe stale legacy data, and any delete failure becomes detached from the write operation.

Proposed fix
     if (options.useBackwardCompatibility) {
       // Clear old entry.
-      _backwardCompatible.delete(key: key, options: options);
+      await _backwardCompatible.delete(key: key, options: options);
     }
   }
🤖 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/lib/src/flutter_secure_storage_windows_ffi.dart`
around lines 148 - 150, The write() implementation currently triggers the legacy
cleanup via _backwardCompatible.delete(key: key, options: options) but does not
await it, allowing write() to return before the legacy entry is removed; update
write() to await the future returned by _backwardCompatible.delete (or return
that awaited result) so the backward-compatibility deletion completes (and any
errors surface) before write() completes—locate the write() method and replace
the fire-and-forget call to _backwardCompatible.delete(...) with an awaited
call.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/test/unit_test.dart-866-917 (1)

866-917: ⚠️ Potential issue | 🟠 Major

Guard these two tests with withFfi(...) too.

These are the only storage tests here that still execute the FFI-backed implementation without the Windows gate. That leaves Linux/macOS CI running Windows-specific code even though the rest of the suite is explicitly skipped there.

Proposed fix
     test(
       'write - new',
-      () async {
+      () => withFfi(() async {
         const key = 'KEY';
         const value = 'VALUE';
 
         var deleteCalled = 0;
         final target = createTarget((call) async {
@@
         final result = await target.read(key: key, options: options);
         expect(result, value);
         expect(deleteCalled, 2);
-      },
+      }),
     );
 
     test(
       'write - overwrite',
-      () async {
+      () => withFfi(() async {
         const key = 'KEY';
         const value1 = 'VALUE1';
         const value2 = 'VALUE2';
@@
         final result = await target.read(key: key, options: options);
         expect(result, value2);
         expect(deleteCalled, 3);
-      },
+      }),
     );
🤖 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/test/unit_test.dart`
around lines 866 - 917, Wrap the two tests titled 'write - new' and 'write -
overwrite' with the FFI gate by calling withFfi(() async { ... }) around their
test bodies so they only run the FFI-backed implementation on Windows; locate
the tests using their names and ensure the existing calls to createTarget,
createOptions, target.write, and target.read are moved inside the withFfi
closure (preserving async/await), e.g. replace each test body with: withFfi(()
async { /* existing body */ });.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart-360-374 (1)

360-374: ⚠️ Potential issue | 🟠 Major

Do not retry every FileSystemException forever.

This loop never exits for permanent failures like access denied, locked files, or an invalid path, so one bad write can hang the caller indefinitely. Restrict retries to the specific missing-file race you want to tolerate, and cap the attempts.

Proposed fix
-        // Loop to handle race condition.
-        while (true) {
+        for (var attempt = 0; attempt < 3; attempt++) {
           try {
             await (await file.create(recursive: true))
                 .writeAsBytes(encryptedText, flush: true);
-            // If success, finish loop.
-            break;
+            break;
           } on FileSystemException catch (e) {
-            // Another process has been deleted a file or parent directory
-            // since previous File.create() call.
-            // We will retry writing.
-            debugPrint(
-              'Reading file has been deleted by another process. $e',
-            );
+            final code = e.osError?.errorCode;
+            final isMissingPathRace =
+                code == ERROR_FILE_NOT_FOUND || code == ERROR_PATH_NOT_FOUND;
+            if (!isMissingPathRace || attempt == 2) {
+              rethrow;
+            }
           }
         }
🤖 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/lib/src/flutter_secure_storage_windows_ffi.dart`
around lines 360 - 374, The infinite retry loop around
file.create()/writeAsBytes should be changed to only retry the race-condition
where the file or parent directory was removed and to stop after a small
maxAttempts; replace the while(true) with a bounded retry (e.g., maxAttempts =
3) and on FileSystemException inspect the exception (e.g., e.osError?.errorCode
or e.message) and only retry for filesystem-not-found/missing-parent conditions,
otherwise rethrow or return the error after logging; reference the current
variables and calls (file.create, writeAsBytes, encryptedText,
FileSystemException catch (e), debugPrint) to locate and update the loop and
error handling.
🟡 Minor comments (11)
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/analysis_options.yaml-1-1 (1)

1-1: ⚠️ Potential issue | 🟡 Minor

Use package:flutter_lints for consistency with the plugin's example app.

The plugin's analysis_options.yaml imports package:lint/package.yaml, while its bundled example app imports package:flutter_lints/flutter.yaml. Although lint is properly declared in dev_dependencies, package:flutter_lints is the recommended choice for Flutter plugins and is already in use within the same package. This inconsistency should be resolved by aligning both to use package:flutter_lints.

🤖 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/analysis_options.yaml`
at line 1, Replace the include in analysis_options.yaml that currently
references "package:lint/package.yaml" with the Flutter-recommended lint bundle
used elsewhere in this package; update the include to use
"package:flutter_lints/flutter.yaml" so the plugin and its example app use the
same lint configuration.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/README.md-3-3 (1)

3-3: ⚠️ Potential issue | 🟡 Minor

Capitalize “Windows” in the package description.

This is a minor docs polish issue in user-facing text.

✏️ Suggested fix
-The windows implementation of [`flutter_secure_storage`][1].
+The Windows implementation of [`flutter_secure_storage`][1].
🤖 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/README.md`
at line 3, The package description currently reads "The windows implementation
of [`flutter_secure_storage`][1]." — update that text to capitalize "Windows"
(i.e., "The Windows implementation of [`flutter_secure_storage`][1].") so the OS
name is properly capitalized in the README package description.
sdk/runanywhere-commons/tests/test_download_orchestrator.cpp-223-240 (1)

223-240: ⚠️ Potential issue | 🟡 Minor

This test can pass without proving that mmproj is actually skipped.

Because both files live in the same directory, a broken implementation still passes whenever the primary .gguf happens to be enumerated first. Please add a deterministic negative case as well—e.g. only mmproj-*.gguf present and assert it is rejected/falls back—or drive the lower-level selector with a stable candidate list.

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

In `@sdk/runanywhere-commons/tests/test_download_orchestrator.cpp` around lines
223 - 240, The test currently can pass spuriously because directory enumeration
order may favor the primary .gguf; update the test in
test_download_orchestrator.cpp to include a deterministic negative case: create
a directory containing only an mmproj-*.gguf (using write_dummy_file) and call
rac_find_model_path_after_extraction expecting it to reject/fail or return the
fallback behavior, and assert that the returned path does not select the mmproj
file; alternatively modify the test to drive the lower-level selector with a
stable candidate list so you deterministically verify that
rac_find_model_path_after_extraction ignores files whose basename starts with
"mmproj-".
docs/building.md-146-157 (1)

146-157: ⚠️ Potential issue | 🟡 Minor

Document the Windows toolchain prerequisites in this section.

The new workflow assumes fvm flutter build windows can run, but the file's prerequisite list is still Android-only. Please add the required Windows setup here (at least Visual Studio Desktop C++, Flutter desktop enabled, and FVM if it's mandatory), otherwise readers will fail before the first command.

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

In `@docs/building.md` around lines 146 - 157, Update the "Windows Voice
Validation" section to list required Windows toolchain prerequisites so readers
can run the shown commands; specifically mention installing Visual Studio with
the "Desktop development with C++" workload (including MSVC, Windows 10/11 SDK
and CMake), enabling Flutter desktop support (flutter config
--enable-windows-desktop), and using FVM if the instructions rely on it (or note
it as optional) before running the shown commands (references: the "Windows
Voice Validation" heading and the fvm flutter build windows command).
examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart-339-341 (1)

339-341: ⚠️ Potential issue | 🟡 Minor

Keep these backend fallback logs platform-aware.

This widget still runs on non-Windows targets, so hardcoding "Windows optional" / "this Windows build" will mislabel Android/iOS/macOS failures and send debugging in the wrong direction. Please make the message platform-aware or keep it generic.

Also applies to: 349-349

🤖 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
339 - 341, The backend fallback logs currently hardcode "Windows" in the
debugPrint messages inside the ONNX registration try/catch (the debugPrint call
that logs '✅ ONNX backend registered (Windows optional)' and the catch that logs
'⚠️ ONNX backend not available on this Windows build: $e'); update these
messages to be platform-aware or generic by using the runtime platform
identifier (e.g., Platform.operatingSystem or similar) or by removing the OS
name so the message applies to all targets, and apply the same change to the
other debugPrint at the second occurrence noted in the diff.
sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart-26-27 (1)

26-27: ⚠️ Potential issue | 🟡 Minor

Remove the duplicate dart_bridge_structured_output.dart import at line 30.

The same library is imported twice—once at line 27 and again at line 30. This triggers duplicate_import in analyzer-enforced builds and should be removed.

Suggested fix
 import 'package:runanywhere/native/dart_bridge_rag.dart';
 import 'package:runanywhere/native/dart_bridge_structured_output.dart';
 import 'package:runanywhere/native/dart_bridge_vlm.dart';
 import 'package:runanywhere/native/ffi_types.dart' show RacVlmImageFormat;
-import 'package:runanywhere/native/dart_bridge_structured_output.dart';
 import 'package:runanywhere/public/configuration/sdk_environment.dart';
🤖 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 26 - 27, Remove the duplicate import of the same library: keep a
single import of 'package:runanywhere/native/dart_bridge_structured_output.dart'
and delete the redundant import line that re-imports it (the duplicate alongside
'package:runanywhere/native/dart_bridge_rag.dart'); ensure only one import
statement for dart_bridge_structured_output.dart remains to resolve the
duplicate_import analyzer error.
sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart-194-201 (1)

194-201: ⚠️ Potential issue | 🟡 Minor

Multi-file progress calculation is inconsistent.

Current math combines file index with byte ratio, so overall progress can be significantly underreported. Track cumulative downloaded bytes across files and derive progress from total bytes instead.

🤖 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 194 - 201, The overallProgress math is incorrect: instead of
combining the file index with the per-file byte ratio, track cumulative bytes
downloaded across files and compute overall progress as
cumulativeDownloadedBytes / totalBytes. In the download loop (where
fileProgress, overallProgress and ModelDownloadProgress are created)
add/maintain a cumulativeDownloadedBytes variable and compute totalBytes by
summing model.downloadSize (falling back to 0) for all files; then set
overallProgress = (cumulativeDownloadedBytes + downloaded) / totalBytes (and
apply the existing *0.9 if you want to keep the stage scaling) when yielding
ModelDownloadProgress so progress reflects actual bytes downloaded across all
files.
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_stub.dart-7-13 (1)

7-13: ⚠️ Potential issue | 🟡 Minor

registerWith() will trigger assertion failure in debug mode.

The constructor contains assert(false, 'Cannot instantiate this class.') (line 8), but registerWith() (line 12) calls the constructor. In debug mode, this causes an assertion failure during plugin registration on non-Windows platforms where this stub is used.

This contradicts the stub's design: all methods are implemented as working no-ops (returning false, empty futures, etc.), suggesting the class is meant to be usable. The assert prevents this in debug mode.

Remove the assert since the stub is designed to function as a no-op fallback:

Suggested fix
  FlutterSecureStorageWindows()
-     : assert(false, 'Cannot instantiate this class.');
+     ; // Stub implementation for non-Windows platforms
🤖 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/lib/src/flutter_secure_storage_windows_stub.dart`
around lines 7 - 13, The FlutterSecureStorageWindows constructor currently
contains assert(false, 'Cannot instantiate this class.'), which causes
registerWith() to fail in debug mode when it sets
FlutterSecureStoragePlatform.instance = FlutterSecureStorageWindows(); remove
the assert and make the constructor a normal no-op constructor so the stub can
be instantiated during plugin registration (update the
FlutterSecureStorageWindows() constructor and leave registerWith() and
FlutterSecureStoragePlatform.instance assignment unchanged).
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/lib/main.dart-150-196 (1)

150-196: ⚠️ Potential issue | 🟡 Minor

Remove the first FutureBuilder; the result is rendered twice.

Both builders listen to the same _future, and the second one already renders Result plus Detail. Keeping both produces duplicate result fields and duplicate spinners after each action.

Proposed fix
-            if (_future != null)
-              FutureBuilder<TestResult>(
-                builder: (context, snapshot) {
-                  if (!snapshot.hasData && !snapshot.hasError) {
-                    return const CircularProgressIndicator();
-                  }
-
-                  resultSummaryFieldController.text =
-                      (snapshot.data?.success ?? false) ? 'SUCCESS' : 'FAIL';
-
-                  return TextField(
-                    controller: resultSummaryFieldController,
-                    decoration: const InputDecoration(label: Text('Result')),
-                  );
-                },
-                future: _future,
-              ),
-
             if (_future != null)
               FutureBuilder<TestResult>(
🤖 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/lib/main.dart`
around lines 150 - 196, Remove the duplicated FutureBuilder: delete the first
FutureBuilder<TestResult> block that reads/writes resultSummaryFieldController
and uses future _future, and keep the second FutureBuilder<TestResult> which
already renders both the "Result" and "Detail" TextFields and handles the
loading/error states; ensure only one FutureBuilder referencing _future remains
and that it updates resultSummaryFieldController and resultDetailFieldController
as in the existing second block.
examples/flutter/RunAnywhereAI/lib/core/services/proxy_settings_service.dart-120-135 (1)

120-135: ⚠️ Potential issue | 🟡 Minor

Persist normalized credentials, not the raw strings.

validate() treats ' ' as empty, but save() uses settings.username.isNotEmpty and will still store whitespace-only credentials. That can silently enable proxy auth with blank-looking values.

Proposed fix
+    final trimmedUsername = settings.username.trim();
+    final trimmedPassword = settings.password.trim();
+
-    if (settings.username.isNotEmpty) {
+    if (trimmedUsername.isNotEmpty) {
       await KeychainHelper.saveString(
         key: _usernameKey(scope),
-        data: settings.username,
+        data: trimmedUsername,
       );
       await KeychainHelper.saveString(
         key: _passwordKey(scope),
-        data: settings.password,
+        data: trimmedPassword,
       );
     } else {
       await KeychainHelper.delete(_usernameKey(scope));
       await KeychainHelper.delete(_passwordKey(scope));
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/lib/core/services/proxy_settings_service.dart`
around lines 120 - 135, The save() flow currently checks
settings.username.isNotEmpty and persists raw strings, so whitespace-only
credentials slip through; update save() to use normalized values (e.g., trimmed
username/password or whatever validate() uses) before checking emptiness,
persist those normalized strings via KeychainHelper.saveString(key:
_usernameKey(scope)/_passwordKey(scope)), delete keys when the normalized values
are empty, and update _current[scope] to store the normalized ProxySettings so
that ProxySettingsValidationResult.valid() and subsequent logic see the cleaned
credentials.
examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp-109-111 (1)

109-111: ⚠️ Potential issue | 🟡 Minor

Check return value of UnregisterClass before updating state.

UnregisterClass may fail for various reasons (e.g., if the module handle or class name doesn't match). Currently, class_registered_ is set to false unconditionally at line 111, even if unregistration fails. This causes the registrar state to diverge from the OS state, breaking subsequent re-registration attempts. The nullptr parameter is correct and equivalent to GetModuleHandle(nullptr) for EXE applications.

Proposed fix
 void WindowClassRegistrar::UnregisterWindowClass() {
-  UnregisterClass(kWindowClassName, nullptr);
-  class_registered_ = false;
+  if (UnregisterClass(kWindowClassName, nullptr) != 0) {
+    class_registered_ = false;
+  }
 }
🤖 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
109 - 111, The UnregisterWindowClass implementation should check the BOOL result
of UnregisterClass(kWindowClassName, nullptr) and only set class_registered_ =
false when UnregisterClass returns nonzero; if it fails, preserve
class_registered_ and handle/log the failure (e.g., call GetLastError() and
record the error) so the registrar state stays consistent; update the
WindowClassRegistrar::UnregisterWindowClass method to perform this check and
error handling.
🧹 Nitpick comments (5)
examples/flutter/RunAnywhereAI/lib/features/models/model_selection_sheet.dart (1)

2-2: Consider using PlatformCapabilityService for consistency.

Other parts of this PR (e.g., chat_interface_view.dart) use PlatformCapabilityService.shared to gate features by platform. Using Platform.isWindows directly here works but creates two different patterns for platform capability checks.

For consistency and centralized control over platform-specific feature availability, consider using the PlatformCapabilityService approach:

if (widget.context == ModelSelectionContext.tts && 
    PlatformCapabilityService.shared.supportsTextToSpeech)

This would require adding a supportsSystemTTS capability or checking supportsTextToSpeech appropriately.

Also applies to: 242-243

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

In
`@examples/flutter/RunAnywhereAI/lib/features/models/model_selection_sheet.dart`
at line 2, Replace direct Platform.isWindows checks in ModelSelectionSheet
(where widget.context == ModelSelectionContext.tts) with the centralized
PlatformCapabilityService check used elsewhere
(PlatformCapabilityService.shared.supportsTextToSpeech or a newly-added
supportsSystemTTS flag); update the conditional(s) in the ModelSelectionSheet
widget (and any helper methods inside that file) to use
PlatformCapabilityService.shared instead of dart:io Platform, and add a
capability accessor to PlatformCapabilityService if needed so the feature gating
is consistent with chat_interface_view.dart.
examples/flutter/RunAnywhereAI/lib/features/voice/text_to_speech_view.dart (1)

130-157: Consider showing loading state during voice refresh.

The _refreshModelState method awaits sdk.RunAnywhere.currentTTSVoice() without setting any loading indicator. For quick responses this is fine, but if the SDK call takes noticeable time, users may see a brief inconsistent state. This is a minor UX consideration rather than a bug.

🤖 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 130 - 157, Add a loading flag during the voice refresh in
_refreshModelState: introduce a boolean state field (e.g. _isRefreshingVoice or
reuse an existing _isLoading) and set it to true with setState before awaiting
sdk.RunAnywhere.currentTTSVoice(), then set it back to false (inside setState)
after the await (or early returns), preserving mounted checks; update the
existing setState blocks to include this flag so the UI can show a loading
indicator while the SDK call is in progress.
sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.cpp (1)

40-50: Incomplete version string for pre-Windows 7 systems.

If IsWindows7OrGreater() returns false (unlikely but possible), version_stream.str() will be just "Windows " with no version identifier. Consider adding an else fallback for completeness.

💡 Proposed fix
     } else if (IsWindows7OrGreater()) {
       version_stream << "7";
+    } else {
+      version_stream << "Unknown";
     }
🤖 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 40 - 50, The getPlatformVersion handler builds a version string
using IsWindows10OrGreater / IsWindows8OrGreater / IsWindows7OrGreater but lacks
a fallback when all are false, producing just "Windows ". Update the logic in
the method handling "getPlatformVersion" (the block using version_stream and
those IsWindows* functions) to append a sensible fallback like "Unknown" or
"pre-7" in an added else branch so
result->Success(flutter::EncodableValue(version_stream.str())) always returns a
complete string.
sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin.cpp (1)

40-50: Same edge case: incomplete version string for pre-Windows 7.

Same issue as in runanywhere_llamacpp_plugin.cpp—consider adding an else fallback for pre-Windows 7 systems.

💡 Proposed fix
     } else if (IsWindows7OrGreater()) {
       version_stream << "7";
+    } else {
+      version_stream << "Unknown";
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin.cpp`
around lines 40 - 50, The getPlatformVersion handler currently builds
version_stream in runanywhere_plugin.cpp but lacks a fallback for pre-Windows 7,
causing an incomplete string; update the if/else chain in the method that checks
method_call.method_name() == "getPlatformVersion" (the block using
IsWindows10OrGreater(), IsWindows8OrGreater(), IsWindows7OrGreater() and
result->Success(flutter::EncodableValue(version_stream.str()))) to add a final
else that appends an appropriate fallback (e.g., "earlier than 7" or the actual
version number) before calling result->Success so older Windows versions return
a meaningful string.
examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart (1)

360-399: Consider dismissing the dialog before showing save-success snackbar.

The current flow shows the success snackbar (lines 388-394) before dismissing the dialog (line 397). If dialogContext.mounted is false but context (outer widget) is still mounted, the snackbar will appear while the dialog is still visible, which may cause a brief visual overlap.

This is a minor UX consideration and the code is functionally correct.

💡 Optional: Dismiss dialog before showing success snackbar
                 await _loadProxyConfiguration(scope);

+                if (dialogContext.mounted) {
+                  Navigator.pop(dialogContext);
+                }
+
                 if (mounted) {
                   ScaffoldMessenger.of(context).showSnackBar(
                     SnackBar(
                       content: Text('${scope.displayName} settings saved'),
                     ),
                   );
                 }
-
-                if (dialogContext.mounted) {
-                  Navigator.pop(dialogContext);
-                }
               },
🤖 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 360 - 399, The success snackbar is shown while the dialog is still
open; dismiss the dialog first to avoid overlap by calling
Navigator.pop(dialogContext) (guarded by dialogContext.mounted) before showing
the success SnackBar via ScaffoldMessenger.of(context). Ensure you still await
_loadProxyConfiguration(scope) as needed, keep the mounted checks for the outer
context, and only show the SnackBar after the dialog has been popped.

Comment on lines +13 to +18
if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
_dup2(_fileno(stdout), 1);
}
if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
_dup2(_fileno(stdout), 2);
}
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:

# First, locate the file
find . -name "utils.cpp" -path "*/flutter_secure_storage_windows/example/windows/runner/*"

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 177


🏁 Script executed:

# If found, read the relevant lines
if [ -f "examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/utils.cpp" ]; then
  cat -n "examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/utils.cpp" | head -30
fi

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 1034


🌐 Web query:

freopen_s MSVC CRT return value success failure

💡 Result:

In MSVC CRT, freopen_s returns 0 on success. On failure, it returns a non-zero error code (errno_t). If an error occurs, it also writes NULL to the stream pointer (unless the stream pointer is NULL) and sets errno to an appropriate error value such as EINVAL for invalid parameters. Example usage: errno_t err = freopen_s(&stream, "file.txt", "w", stderr); if (err != 0) { // Handle error } This behavior is consistent across Microsoft documentation, with code samples checking for err != 0 to detect failure.

Citations:


🌐 Web query:

_dup2 function Windows MSVC parameters file descriptor

💡 Result:

The _dup2 function in Windows MSVC (Microsoft Visual C++) duplicates a file descriptor. It reassigns file descriptor fd2 to refer to the same open file as fd1. Syntax: int _dup2(int fd1, int fd2); Parameters: - fd1: File descriptor referring to an open file (source). - fd2: Any file descriptor (target, which will be reassigned; if already open, it is closed first unless fd1 == fd2). Return value: 0 on success, -1 on error (sets errno to EBADF for invalid descriptor or EMFILE if no descriptors available). Include <io.h>. Use _fileno to get file descriptor from FILE*. Note: POSIX dup2 is deprecated; use _dup2. Not supported in UWP apps without redirection.

Citations:


Fix console redirection logic: inverted condition checks and incorrect stderr duplication.

freopen_s returns 0 on success, but the conditions execute _dup2 only on failure. Additionally, line 17 duplicates stdout into fd 2 instead of stderr. Both issues prevent proper console output redirection.

Proposed fix
-    if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
+    if (freopen_s(&unused, "CONOUT$", "w", stdout) == 0) {
       _dup2(_fileno(stdout), 1);
     }
-    if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
-      _dup2(_fileno(stdout), 2);
+    if (freopen_s(&unused, "CONOUT$", "w", stderr) == 0) {
+      _dup2(_fileno(stderr), 2);
     }
📝 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 (freopen_s(&unused, "CONOUT$", "w", stdout)) {
_dup2(_fileno(stdout), 1);
}
if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
_dup2(_fileno(stdout), 2);
}
if (freopen_s(&unused, "CONOUT$", "w", stdout) == 0) {
_dup2(_fileno(stdout), 1);
}
if (freopen_s(&unused, "CONOUT$", "w", stderr) == 0) {
_dup2(_fileno(stderr), 2);
}
🤖 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/utils.cpp`
around lines 13 - 18, The console redirection logic is inverted and duplicates
the wrong stream: change both freopen_s condition checks to test for success
(compare return value == 0) instead of truthy, and when reopening stderr use
_dup2(_fileno(stderr), 2) (not duplicating stdout into fd 2). Update the blocks
around freopen_s(&unused, "CONOUT$", "w", stdout) and freopen_s(&unused,
"CONOUT$", "w", stderr) accordingly so successful reopen leads to the
corresponding _dup2 call for stdout and stderr.

Comment on lines +50 to +55
std::optional<std::string> GetStringArg(
const std::string &param,
const flutter::EncodableMap *args);

// Derive the key for a value given a method argument map.
std::optional<std::string> FlutterSecureStorageWindowsPlugin::GetValueKey(const flutter::EncodableMap *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 | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Is a member declaration like std::optionalstd::string ClassName::method(...);valid inside a C++ class definition, or is theClassName:: extra qualification rejected by the language?

💡 Result:

No, a member declaration like std::optionalstd::string ClassName::method(...); is not valid inside a C++ class definition. The ClassName:: extra qualification is rejected by the language.

Citations:


Remove the extra qualification from the member declaration.

FlutterSecureStorageWindowsPlugin::GetValueKey(...) is declared inside the class body. C++ does not allow qualified names in member declarations—this is a compilation error. Change the declaration to GetValueKey(...) without the class scope prefix.

Suggested fix
-    std::optional<std::string> FlutterSecureStorageWindowsPlugin::GetValueKey(const flutter::EncodableMap *args);
+    std::optional<std::string> GetValueKey(const flutter::EncodableMap *args);
📝 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
std::optional<std::string> GetStringArg(
const std::string &param,
const flutter::EncodableMap *args);
// Derive the key for a value given a method argument map.
std::optional<std::string> FlutterSecureStorageWindowsPlugin::GetValueKey(const flutter::EncodableMap *args);
std::optional<std::string> GetStringArg(
const std::string &param,
const flutter::EncodableMap *args);
// Derive the key for a value given a method argument map.
std::optional<std::string> GetValueKey(const flutter::EncodableMap *args);
🤖 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 50 - 55, The member declaration for GetValueKey in the class
incorrectly uses a qualified name; remove the class scope qualifier so the
declaration becomes a plain member declaration. Locate
FlutterSecureStorageWindowsPlugin::GetValueKey(...) in the class body and change
it to GetValueKey(const flutter::EncodableMap *args); also verify the nearby
helper declaration GetStringArg(...) remains unchanged and that the definition
outside the class still uses the qualified name
FlutterSecureStorageWindowsPlugin::GetValueKey(...) to match.

Comment on lines +216 to +264
SHGetKnownFolderPath(FOLDERID_RoamingAppData,KF_FLAG_DEFAULT,NULL,&appdataPath);

if (nameBuffer == NULL) {
return ERROR_OUTOFMEMORY;
}

resVal = GetModuleFileName(NULL,nameBuffer,MAX_PATH);
if (resVal == 0) {
return GetLastError();
}

versionInfoSize = GetFileVersionInfoSize(nameBuffer, NULL);
if (versionInfoSize != 0) {
infoBuffer = (char*) calloc(versionInfoSize,sizeof(char));
if (infoBuffer == NULL) {
return ERROR_OUTOFMEMORY;
}
if (GetFileVersionInfo(nameBuffer, 0, versionInfoSize, infoBuffer) == 0) {
free(infoBuffer);
infoBuffer = NULL;
}
else {

if (VerQueryValue(infoBuffer, TEXT("\\StringFileInfo\\040904e4\\CompanyName"), &queryVal, &queryLen) != 0) {
companyName = SanitizeDirString(std::wstring((const TCHAR*)queryVal));
}
else if (VerQueryValue(infoBuffer, TEXT("\\StringFileInfo\\040904b0\\CompanyName"), &queryVal, &queryLen) != 0) {
companyName = SanitizeDirString(std::wstring((const TCHAR*)queryVal));
}
else {
companyName = L"placeholder_company";
}
if (VerQueryValue(infoBuffer, TEXT("\\StringFileInfo\\040904e4\\ProductName"), &queryVal, &queryLen) != 0) {
productName = SanitizeDirString(std::wstring((const TCHAR*)queryVal));
}
else if (VerQueryValue(infoBuffer, TEXT("\\StringFileInfo\\040904b0\\ProductName"), &queryVal, &queryLen) != 0) {
productName = SanitizeDirString(std::wstring((const TCHAR*)queryVal));
}
else {
productName = L"placeholder_product";
}
}
stream << appdataPath << "\\" << companyName << "\\" << productName;
path = stream.str();
}
else {
return GetLastError();
}
return ERROR_SUCCESS;
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

Don't return success with an empty app-support path.

If GetFileVersionInfo(...) fails, companyName and productName stay empty but the function still returns ERROR_SUCCESS and hands callers a path rooted at roaming AppData. Downstream ReadAll/DeleteAll then enumerate or delete unrelated .secure files outside this app.

🤖 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 216 - 264, The function currently proceeds to build and return an
app-support path even when GetFileVersionInfo fails, leaving
companyName/productName empty and returning ERROR_SUCCESS; change the control
flow in the block that calls GetFileVersionInfo so that if GetFileVersionInfo
returns 0 you free infoBuffer (if allocated) and return a failure code (e.g.,
GetLastError() or ERROR_RESOURCE_DATA_NOT_FOUND) instead of falling through;
ensure companyName and productName are only used after successful VerQueryValue
checks (or set to safe placeholders only after successful version info parsing),
and keep the existing cleanup (free(infoBuffer)) before every early return to
avoid leaks — look for the GetFileVersionInfo call, the infoBuffer allocation,
the VerQueryValue branches, the stream/ path construction, and the final return
ERROR_SUCCESS to implement this fix.

Comment on lines +448 to +452
catch (DWORD e)
{
auto str_code = this->GetErrorString(e);
result->Error("Exception encountered: " + str_code, method);
}
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

Catch the exceptions this file actually throws.

Write() throws std::runtime_error, but HandleMethodCall only catches DWORD. Any write-side failure will therefore escape the method channel handler and can terminate the runner instead of surfacing a Dart error.

Suggested fix
     catch (DWORD e)
     {
       auto str_code = this->GetErrorString(e);
       result->Error("Exception encountered: " + str_code, method);
     }
+    catch (const std::exception& e)
+    {
+      result->Error("Exception encountered", e.what());
+    }
+    catch (...)
+    {
+      result->Error("Exception encountered", "unknown native exception");
+    }
   }
🤖 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 448 - 452, The catch in HandleMethodCall currently only catches
DWORD but Write() throws std::runtime_error (and other std::exception types), so
change the error handling around the Write() call in HandleMethodCall to also
catch std::exception (e.g., catch (const std::exception& e)) and convert
e.what() into a Dart error via result->Error (similar to how the DWORD path uses
GetErrorString), and optionally keep a separate catch for DWORD to preserve
GetErrorString semantics; ensure both paths call result->Error with the method
name and an informative message so C++ exceptions are surfaced to Dart instead
of escaping.

if (!PathExists(appSupportPath)) {
MakePath(appSupportPath);
}
fs = std::basic_ofstream<BYTE>(appSupportPath + L"\\" + std::wstring(key.begin(), key.end()) + L".secure", std::ios::binary | std::ios::trunc);
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

Sanitize or encode keys before using them as filenames.

key is concatenated directly into ...\\${key}.secure. Inputs like ../foo or a\\b can escape the app-support directory and turn write, read, delete, and containsKey into arbitrary file operations.

Also applies to: 615-615, 798-798, 880-880

🤖 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`
at line 549, The code uses the raw key string to build filenames (e.g., the
ofstream call at the shown diff) which allows path traversal; create and use a
sanitizer/encoder for keys (e.g., sanitizeKeyToFilename or encodeKeyForStorage)
and call it from the write/read/delete/containsKey implementations so all file
ops use the sanitized name. The sanitizer should reject or normalize path
separators and ".." (replace '\' and '/' with '_' or percent-encode them),
restrict to a safe char set (or alternatively use a stable hash like SHA256 hex
of the key as the filename and store a mapping if needed), and then construct
the path using the sanitized/hashed result instead of std::wstring(key.begin(),
key.end()).

Comment on lines +745 to +748
hFile = FindFirstFile((appSupportPath + L"\\*.secure").c_str(), &searchRes);
if (hFile == INVALID_HANDLE_VALUE) {
return flutter::EncodableMap();
}
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

Don't skip legacy storage when there are no .secure files.

Both methods return immediately when FindFirstFile misses file-backed entries, so a legacy-only install makes readAll() return {} and deleteAll() leave Credential Manager data untouched. That breaks the backward-compat paths this PR is testing.

Also applies to: 831-833

🤖 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 745 - 748, The code returns early when
FindFirstFile((appSupportPath + L"\\*.secure")...) yields INVALID_HANDLE_VALUE,
which causes readAll() and deleteAll() to skip legacy Credential Manager
handling; instead, don't return from the function on INVALID_HANDLE_VALUE —
treat it as "no file-backed entries" and continue to the existing legacy code
path that reads/deletes from Credential Manager; specifically update the blocks
around FindFirstFile, hFile and searchRes in the functions readAll() and
deleteAll() so that if hFile == INVALID_HANDLE_VALUE you skip the file iteration
logic but do not return (allow the subsequent legacy-processing code to run),
and ensure any opened handles are closed (FindClose) when appropriate.

Comment on lines +64 to +68
switch (message) {
case WM_FONTCHANGE:
flutter_controller_->engine()->ReloadSystemFonts();
break;
}
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

Potential null pointer dereference in WM_FONTCHANGE handler.

When flutter_controller_ is null, the check on line 55 fails and execution falls through to the switch statement. However, line 66 accesses flutter_controller_->engine() without a null check, which would cause a crash if WM_FONTCHANGE is received when flutter_controller_ is null.

🐛 Proposed fix to guard against null controller
   switch (message) {
     case WM_FONTCHANGE:
-      flutter_controller_->engine()->ReloadSystemFonts();
+      if (flutter_controller_) {
+        flutter_controller_->engine()->ReloadSystemFonts();
+      }
       break;
   }
📝 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
switch (message) {
case WM_FONTCHANGE:
flutter_controller_->engine()->ReloadSystemFonts();
break;
}
switch (message) {
case WM_FONTCHANGE:
if (flutter_controller_) {
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/windows/runner/flutter_window.cpp` around
lines 64 - 68, The WM_FONTCHANGE case can dereference a null
flutter_controller_; update the handler so before calling
flutter_controller_->engine()->ReloadSystemFonts() you verify
flutter_controller_ is non-null (and ideally flutter_controller_->engine() is
non-null) and only call ReloadSystemFonts when those checks pass; modify the
switch/WM_FONTCHANGE branch in flutter_window.cpp to guard the call to
ReloadSystemFonts using the flutter_controller_ and engine null checks.

Comment on lines +10 to +22
void CreateAndAttachConsole() {
if (::AllocConsole()) {
FILE *unused;
if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
_dup2(_fileno(stdout), 1);
}
if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
_dup2(_fileno(stdout), 2);
}
std::ios::sync_with_stdio();
FlutterDesktopResyncOutputStreams();
}
}
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

Bug: freopen_s return value check is inverted.

freopen_s returns 0 on success and a non-zero error code on failure. The current logic calls _dup2 only when freopen_s fails, which is the opposite of the intended behavior. The _dup2 calls should execute on success (when freopen_s returns 0).

🐛 Proposed fix
 void CreateAndAttachConsole() {
   if (::AllocConsole()) {
     FILE *unused;
-    if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
+    if (freopen_s(&unused, "CONOUT$", "w", stdout) == 0) {
       _dup2(_fileno(stdout), 1);
     }
-    if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
+    if (freopen_s(&unused, "CONOUT$", "w", stderr) == 0) {
       _dup2(_fileno(stdout), 2);
     }
     std::ios::sync_with_stdio();
     FlutterDesktopResyncOutputStreams();
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/flutter/RunAnywhereAI/windows/runner/utils.cpp` around lines 10 -
22, In CreateAndAttachConsole(), the freopen_s return checks are inverted and
the stderr dup uses stdout; change the logic to call _dup2 only when freopen_s
returns 0 (success) and for the second freopen_s use _fileno(stderr) with target
2; specifically update the two conditions around freopen_s(&unused, "CONOUT$",
"w", stdout) and freopen_s(&unused, "CONOUT$", "w", stderr) to test for == 0 and
then call _dup2(_fileno(stdout), 1) and _dup2(_fileno(stderr), 2) respectively.

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
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

Build root is pointing at scripts/, not sdk/runanywhere-commons.

Lines 23-25 make ROOT equal to %~dp0, so Line 56 resolves download-sherpa-onnx.bat under scripts\scripts\windows, and Line 60 points CMake at the scripts folder instead of the project root. This entrypoint will fail before the actual Windows build starts.

Proposed fix
-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"
+for %%I in ("%~dp0.") do set "SCRIPT_HOME=%%~fI"
+for %%I in ("%SCRIPT_HOME%\..") do set "ROOT=%%~fI"
+set "WINDOWS_SCRIPT_DIR=%SCRIPT_HOME%\windows"
+set "BUILD_DIR=%ROOT%\build-windows-x64"
+set "DIST_DIR=%ROOT%\dist\windows\x64"
📝 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
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"
for %%I in ("%~dp0.") do set "SCRIPT_HOME=%%~fI"
for %%I in ("%SCRIPT_HOME%\..") do set "ROOT=%%~fI"
set "WINDOWS_SCRIPT_DIR=%SCRIPT_HOME%\windows"
set "BUILD_DIR=%ROOT%\build-windows-x64"
set "DIST_DIR=%ROOT%\dist\windows\x64"
🤖 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 23 - 27, The
build root is being set to the script folder so ROOT ends up as
sdk/runanywhere-commons/scripts and paths like download-sherpa-onnx.bat and
CMake resolve to scripts\scripts\windows; change the logic that sets
SCRIPT_HOME/ROOT so ROOT points to the project root (one directory up from the
script folder) and then derive WINDOWS_SCRIPT_DIR as %ROOT%\scripts\windows (and
BUILD_DIR/DIST_DIR from %ROOT%); update the variables SCRIPT_HOME and ROOT (and
any use of WINDOWS_SCRIPT_DIR) to compute the parent directory of the current
script location so CMake and download-sherpa-onnx.bat resolve under the
repository root rather than scripts\scripts\windows.

Comment on lines +80 to +89
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)
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

Critical: Duplicate elseif(RAC_PLATFORM_WINDOWS) blocks - second block is unreachable.

Lines 104-117 contain a second elseif(RAC_PLATFORM_WINDOWS) that will never execute because the first block at lines 80-89 matches first. This appears to be a merge error.

The unreachable block sets GGML_OPENMP OFF which is missing from the first block. You likely need to merge these settings.

🐛 Proposed fix: merge Windows platform settings
 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
     ...
-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()

Also applies to: 104-117

🤖 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 80
- 89, There is a duplicate elseif(RAC_PLATFORM_WINDOWS) block causing the second
block (which sets GGML_OPENMP OFF) to be unreachable; open the CMakeLists and
merge the Windows settings by consolidating both elseif(RAC_PLATFORM_WINDOWS)
blocks into one so the Windows branch sets GGML_METAL, GGML_VULKAN, GGML_CUDA,
GGML_OPENCL, GGML_HIPBLAS, GGML_SYCL, GGML_KOMPUTE, GGML_RPC, GGML_NATIVE and
also GGML_OPENMP OFF (use CACHE BOOL "" FORCE for each variable to match
existing style), removing the redundant elseif to avoid the unreachable code.

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: 3

🧹 Nitpick comments (11)
sdk/runanywhere-commons/scripts/build-windows.bat (1)

7-16: Unknown arguments are silently ignored.

The parser falls through all if clauses and shifts without flagging unrecognized input, so typos like build-windows.bat onxx silently default to BACKENDS=llamacpp instead of warning the user. Consider printing the usage and exiting non-zero when no clause matches.

🤖 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
argument parsing loop at label :parse_args silently ignores unknown flags (it
always shifts and continues), which can cause typos like "onxx" to be accepted;
update the loop in :parse_args to detect when none of the if /I "%~1"=="..."
branches match and in that case call the usage routine (goto usage or print
usage) and exit with a non-zero code so the script fails fast; reference the
BACKENDS and CLEAN variables and ensure existing branches (all, llamacpp, onnx,
--clean, -h, --help) still set those variables before shift/goto args_done.
sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart (3)

221-267: Dead code: _authenticateWithBackend is now unreachable.

The // ignore: unused_element annotation confirms this method has no callers. Since initializeWithParams no longer invokes it (auth appears to be handled elsewhere), consider removing it outright rather than suppressing the warning — suppressed dead code tends to drift out of sync with the real auth flow. If it's intentionally kept as reference for a future code path, add a short comment explaining why.

🤖 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 221 - 267, The private method _authenticateWithBackend is
dead/unreferenced (the // ignore: unused_element hints it) because
initializeWithParams no longer calls it; remove the entire
_authenticateWithBackend function and its ignore annotation to avoid drifting,
or if you intend to keep it as a reference, replace the ignore with a concise
comment explaining its purpose and future use (mentioning DartBridgeAuth,
DartBridgeDevice.instance.getDeviceId, and HTTPService.shared.setToken so
reviewers can locate related auth flow). Ensure any removed imports or
references are cleaned up.

1817-1827: Unused model parameter in _resolveVLMModelFilePath.

After delegating to resolveVlmMainModelPath, the ModelInfo model argument is no longer used. Either drop it from the signature (and the call site at line 1455) or use it to prefer an exact filename match when available (e.g., if model.name/metadata hints at the main gguf basename), which would be more robust than "first non-mmproj .gguf wins" when a folder contains multiple .gguf files.

🤖 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 1817 - 1827, The _resolveVLMModelFilePath function currently
accepts a ModelInfo model parameter that is never used; either remove the unused
parameter from the signature and update all call sites that pass a ModelInfo
(e.g., the caller that invokes _resolveVLMModelFilePath) to stop providing that
argument, or enhance _resolveVLMModelFilePath to use model (for example use
model.name or other metadata to prefer an exact gguf basename match before
falling back to resolveVlmMainModelPath) so it disambiguates when multiple .gguf
files exist; make the change inside _resolveVLMModelFilePath and adjust callers
accordingly.

2240-2247: Document the HTTP client factory ownership contract and null-clearing behavior.

The factory-returned http.Client will be unconditionally closed by the SDK after each download completes (in the finally block), regardless of whether it's a fresh client or a caller-provided one. This means:

  1. Explicitly document that passing null clears the factory so callers know how to reset to the default behavior.
  2. Document the ownership transfer: Once the SDK receives a client from the factory, it takes ownership and closes it. If callers return a shared long-lived client, the SDK will close it, which could break the caller's subsequent use of that client.

Consider either:

  • Documenting the ownership contract explicitly (SDK closes all clients from the factory), or
  • Wrapping factory-returned clients to make close() a no-op, preserving caller ownership.
🤖 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 2240 - 2247, Update the configureDownloadHttpClientFactory doc
comment to explicitly state two behaviors: passing null clears the custom
factory (resets to default), and any http.Client returned by the provided
factory is owned by the SDK and will be closed by the SDK after each download
(so callers must not return shared/long‑lived clients). Reference the public API
name configureDownloadHttpClientFactory and the underlying
ModelDownloadService.shared.configureClientFactory behavior; alternatively note
as an option that callers can return a wrapped http.Client with a no-op close if
they need to preserve ownership.
sdk/runanywhere-flutter/packages/runanywhere/lib/internal/vlm_file_resolution.dart (2)

12-25: Silent catch (_) may hide real errors.

Both helpers swallow all exceptions and return null, which makes permission errors, I/O failures, and programming mistakes indistinguishable from "no match". At minimum, log the caught error (the SDK already has SDKLogger) so operators can diagnose missing-model reports. Returning null as the external contract is fine — just don't drop the diagnostic signal.

-  } catch (_) {
-    return null;
+  } catch (e, st) {
+    SDKLogger('VLMFileResolution').debug('resolveVlmMainModelPath failed: $e\n$st');
+    return null;
   }

Also applies to: 32-45

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

In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/internal/vlm_file_resolution.dart`
around lines 12 - 25, The current catch (_) swallows all exceptions; change it
to catch (e, st) and log the error and stack using the SDKLogger before
returning null so permission/I/O/programming errors are visible; update the
try/catch surrounding dir.list() and the analogous block around the other helper
(the block that builds ggufPaths/mainModelPaths using vlmPathBasename) to call
SDKLogger.error (or the SDK's logging method) with a clear message, the
exception e and stack st, then return null as the external result.

3-5: Consider using package:path for basename extraction.

vlmPathBasename works for typical cases but has edge cases: trailing separators yield an empty string (e.g., C:\foo\''), and mixed separators are handled only by simple replacement. Dart's package:path (p.basename) is platform-aware and more robust. Since package:path is already a direct dependency in this project, the refactor is straightforward.

Proposed change
+import 'package:path/path.dart' as p;

-String vlmPathBasename(String path) {
-  return path.replaceAll('\\', '/').split('/').last;
-}
+String vlmPathBasename(String path) => p.basename(path);

Note: p.basename uses the platform's separator by default. If cross-platform normalization of Windows-style paths on POSIX is required (as indicated by existing tests), use p.Context(style: Style.windows).basename(path) conditionally, or keep a fallback that normalizes \ to / first.

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

In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/internal/vlm_file_resolution.dart`
around lines 3 - 5, Replace the hand-rolled basename logic in vlmPathBasename
with package:path's basename to handle edge cases (trailing separators, mixed
separators, platform differences); update vlmPathBasename to call
p.basename(path) or, if Windows-style normalization is required, use
p.Context(style: Style.windows).basename(path) (import p from
'package:path/path.dart') so basename extraction is robust and platform-aware.
sdk/runanywhere-flutter/packages/runanywhere/test/vlm_file_resolution_test.dart (1)

6-48: LGTM — solid coverage for the new helpers.

The temp-dir setup, teardown via addTearDown, and assertions around the main/mmproj/archive files correctly validate the selection logic (especially the mmproj exclusion). One optional addition: a case verifying that passing a file path (not directory) to resolveVlmMainModelPath still resolves via the parent directory, since that branch is part of the implementation's contract but isn't exercised here.

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

In
`@sdk/runanywhere-flutter/packages/runanywhere/test/vlm_file_resolution_test.dart`
around lines 6 - 48, Add a test that calls resolveVlmMainModelPath with a file
path (not a directory) to exercise the branch that treats file inputs by using
their parent directory; in the existing group 'VLM file resolution' create a
temp file (e.g., mainModel) in the temp dir, write contents, then pass
mainModel.path into resolveVlmMainModelPath and assert the returned path equals
mainModel.path (also assert findVlmMmprojPath when appropriate) so the code path
in resolveVlmMainModelPath that uses parent directory is covered.
docs/superpowers/plans/2026-04-17-windows-vision-restoration.md (1)

635-649: Plan drifted from shipped implementation (non-blocking).

A few spots in the plan no longer match the landed code, worth a quick sync pass if this doc is intended to stay accurate:

  • disposeCamera in the plan is Future<void> async (lines 635–640); the shipped view model uses void disposeCamera() with an unawaited session?.dispose().
  • The error panel snippet (lines 874–891) is the older red variant; shipped vlm_camera_view.dart uses the orange/info-outline container.
  • The shipped test file grew additional cases (dispose/init races, dispose-during-capture, initialize-error cleanup) beyond what Step 1 enumerates.

Since this is a planning doc, this is informational rather than a defect.

Also applies to: 874-891

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

In `@docs/superpowers/plans/2026-04-17-windows-vision-restoration.md` around lines
635 - 649, Update the plan doc to match the shipped implementation: change the
disposeCamera signature and behavior to match the view model's void
disposeCamera() with unawaited session?.dispose(), update the error panel
snippet to use the shipped orange/info-outline container as in
vlm_camera_view.dart, and expand the tests section to list the additional
shipped test cases (dispose/init races, dispose-during-capture, initialize-error
cleanup) so the plan and test checklist reflect the current codebase.
examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dart (2)

33-44: handle! cast will produce an unhelpful error if the device didn't come from this backend.

device.handle! as CameraDescription fails with a generic null-check or type-cast exception if a test fake or a different backend's VisionCameraDevice is ever passed. A guarded cast with a descriptive StateError/ArgumentError makes mis-wiring obvious; alternatively, narrow handle to CameraDescription? on the concrete constructor path.

🤖 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, The createSession implementation in
CameraPluginVisionCameraBackend currently does a blind cast device.handle! as
CameraDescription which produces opaque null-check/type-cast errors if a
non-plugin device or a test double is passed; change createSession to validate
the handle first (e.g., check device.handle is CameraDescription and not null)
and throw a clear ArgumentError or StateError with a descriptive message
(referencing the device.identity or id) if the handle is missing or of the wrong
type, then pass the safely-casted CameraDescription into
_CameraPluginVisionCameraSession/CameraController.

4-4: The switch expression lacks a default arm; VisionCameraLensDirection.unknown is unused and the mapping is not forward-compatible.

The unknown variant is defined but never produced. The switch currently requires CameraLensDirection to have exactly {front, back, external} — if the upstream camera package adds a new value in a future version, this code will fail to compile. Adding a default arm that maps to unknown both utilizes the unused variant and future-proofs the mapping.

♻️ 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 mapping from the upstream CameraLensDirection to our enum
VisionCameraLensDirection currently omits a default switch arm, leaving
VisionCameraLensDirection.unknown unused and making the code brittle; update the
switch (where you map CameraLensDirection -> VisionCameraLensDirection) to
include a default/catch-all case that returns VisionCameraLensDirection.unknown
so the unknown variant is used and the mapping remains forward-compatible if
CameraLensDirection adds values later.
examples/flutter/RunAnywhereAI/lib/features/vision/vlm_camera_view.dart (1)

350-363: Simplify the snackbar unawaited wrapping.

The .then((_) => null) tail is a no-op since .closed already returns a Future; unawaited(...) accepts it directly. This keeps the call site slimmer.

♻️ Proposed simplification
-        unawaited(Clipboard.setData(
-            ClipboardData(text: _viewModel.currentDescription)));
-        unawaited(
-          ScaffoldMessenger.of(context)
-              .showSnackBar(
-                const SnackBar(
-                  content: Text('Description copied to clipboard'),
-                  duration: Duration(seconds: 2),
-                ),
-              )
-              .closed
-              .then((_) => null),
-        );
+        unawaited(Clipboard.setData(
+          ClipboardData(text: _viewModel.currentDescription),
+        ));
+        ScaffoldMessenger.of(context).showSnackBar(
+          const SnackBar(
+            content: Text('Description copied to clipboard'),
+            duration: Duration(seconds: 2),
+          ),
+        );
🤖 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_camera_view.dart`
around lines 350 - 363, The snackbar call wraps
ScaffoldMessenger.of(context).showSnackBar(...).closed with an unnecessary
.then((_) => null) before passing to unawaited; remove the .then((_) => null)
and pass the Future returned by .closed directly to unawaited so the code
becomes unawaited(ScaffoldMessenger.of(context).showSnackBar(...).closed); keep
the Clipboard.setData unawaited(Clipboard.setData(ClipboardData(text:
_viewModel.currentDescription))) as-is and ensure you reference the same
SnackBar construction and _viewModel.currentDescription when editing.
🤖 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/features/vision/services/vision_camera_backend.dart`:
- Line 41: The code forces ImageFormatGroup.bgra8888 for camera frames; add a
runtime check after camera initialization (in the vision_camera_backend code
that sets imageFormatGroup) to detect the actual format provided by the platform
(especially on Windows/camera_windows) and log or adapt behavior if it differs;
specifically, inspect the CameraController or returned
CameraDescription/previewFormat to confirm the format group, emit a warning if
it's not bgra8888, and conditionally adjust downstream pixel handling or fall
back to a supported format to avoid misinterpreting BGRA/RGBA channel order when
Windows frame streaming is enabled.

In `@examples/flutter/RunAnywhereAI/lib/features/vision/vlm_view_model.dart`:
- Around line 283-285: The silent catch in the auto-stream block should surface
errors instead of swallowing them: change the catch (_) in the auto-stream
try/catch to capture the exception (e, stack) and assign a user-visible message
to the existing _error field (or call the ViewModel's error setter), and log the
error via the existing logger or debugPrint so failures (camera revoked, model
unloaded, inference errors) are recorded; also ensure any LIVE state toggles
(the code around the auto-stream start/stop functions) reflect the failure so
the UI indicator does not remain stuck on LIVE.
- Around line 56-108: The initializeCamera method can be run concurrently and
leak sessions; add an in-flight guard or token to serialize/ignore overlapping
calls so only one initialization proceeds and any sessions created by earlier,
superseded calls are disposed. Concretely: introduce a private flag or
initialization token (e.g., _isInitializing or _initializeToken) checked at the
start of initializeCamera and set for the lifetime of the async operation;
before assigning _cameraSession = nextSession only allow the assignment when the
token/flag still matches (otherwise dispose nextSession); also ensure any
previous _cameraSession is awaited/disposed before creating a new session (use
await _cameraSession?.dispose()), and clear the flag/token and notify listeners
in all success/failure/early-return paths; reference initializeCamera,
_cameraSession, _isDisposed, and _safeNotifyListeners when applying these
changes.

---

Nitpick comments:
In `@docs/superpowers/plans/2026-04-17-windows-vision-restoration.md`:
- Around line 635-649: Update the plan doc to match the shipped implementation:
change the disposeCamera signature and behavior to match the view model's void
disposeCamera() with unawaited session?.dispose(), update the error panel
snippet to use the shipped orange/info-outline container as in
vlm_camera_view.dart, and expand the tests section to list the additional
shipped test cases (dispose/init races, dispose-during-capture, initialize-error
cleanup) so the plan and test checklist reflect the current codebase.

In
`@examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dart`:
- Around line 33-44: The createSession implementation in
CameraPluginVisionCameraBackend currently does a blind cast device.handle! as
CameraDescription which produces opaque null-check/type-cast errors if a
non-plugin device or a test double is passed; change createSession to validate
the handle first (e.g., check device.handle is CameraDescription and not null)
and throw a clear ArgumentError or StateError with a descriptive message
(referencing the device.identity or id) if the handle is missing or of the wrong
type, then pass the safely-casted CameraDescription into
_CameraPluginVisionCameraSession/CameraController.
- Line 4: The mapping from the upstream CameraLensDirection to our enum
VisionCameraLensDirection currently omits a default switch arm, leaving
VisionCameraLensDirection.unknown unused and making the code brittle; update the
switch (where you map CameraLensDirection -> VisionCameraLensDirection) to
include a default/catch-all case that returns VisionCameraLensDirection.unknown
so the unknown variant is used and the mapping remains forward-compatible if
CameraLensDirection adds values later.

In `@examples/flutter/RunAnywhereAI/lib/features/vision/vlm_camera_view.dart`:
- Around line 350-363: The snackbar call wraps
ScaffoldMessenger.of(context).showSnackBar(...).closed with an unnecessary
.then((_) => null) before passing to unawaited; remove the .then((_) => null)
and pass the Future returned by .closed directly to unawaited so the code
becomes unawaited(ScaffoldMessenger.of(context).showSnackBar(...).closed); keep
the Clipboard.setData unawaited(Clipboard.setData(ClipboardData(text:
_viewModel.currentDescription))) as-is and ensure you reference the same
SnackBar construction and _viewModel.currentDescription when editing.

In `@sdk/runanywhere-commons/scripts/build-windows.bat`:
- Around line 7-16: The argument parsing loop at label :parse_args silently
ignores unknown flags (it always shifts and continues), which can cause typos
like "onxx" to be accepted; update the loop in :parse_args to detect when none
of the if /I "%~1"=="..." branches match and in that case call the usage routine
(goto usage or print usage) and exit with a non-zero code so the script fails
fast; reference the BACKENDS and CLEAN variables and ensure existing branches
(all, llamacpp, onnx, --clean, -h, --help) still set those variables before
shift/goto args_done.

In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/internal/vlm_file_resolution.dart`:
- Around line 12-25: The current catch (_) swallows all exceptions; change it to
catch (e, st) and log the error and stack using the SDKLogger before returning
null so permission/I/O/programming errors are visible; update the try/catch
surrounding dir.list() and the analogous block around the other helper (the
block that builds ggufPaths/mainModelPaths using vlmPathBasename) to call
SDKLogger.error (or the SDK's logging method) with a clear message, the
exception e and stack st, then return null as the external result.
- Around line 3-5: Replace the hand-rolled basename logic in vlmPathBasename
with package:path's basename to handle edge cases (trailing separators, mixed
separators, platform differences); update vlmPathBasename to call
p.basename(path) or, if Windows-style normalization is required, use
p.Context(style: Style.windows).basename(path) (import p from
'package:path/path.dart') so basename extraction is robust and platform-aware.

In `@sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart`:
- Around line 221-267: The private method _authenticateWithBackend is
dead/unreferenced (the // ignore: unused_element hints it) because
initializeWithParams no longer calls it; remove the entire
_authenticateWithBackend function and its ignore annotation to avoid drifting,
or if you intend to keep it as a reference, replace the ignore with a concise
comment explaining its purpose and future use (mentioning DartBridgeAuth,
DartBridgeDevice.instance.getDeviceId, and HTTPService.shared.setToken so
reviewers can locate related auth flow). Ensure any removed imports or
references are cleaned up.
- Around line 1817-1827: The _resolveVLMModelFilePath function currently accepts
a ModelInfo model parameter that is never used; either remove the unused
parameter from the signature and update all call sites that pass a ModelInfo
(e.g., the caller that invokes _resolveVLMModelFilePath) to stop providing that
argument, or enhance _resolveVLMModelFilePath to use model (for example use
model.name or other metadata to prefer an exact gguf basename match before
falling back to resolveVlmMainModelPath) so it disambiguates when multiple .gguf
files exist; make the change inside _resolveVLMModelFilePath and adjust callers
accordingly.
- Around line 2240-2247: Update the configureDownloadHttpClientFactory doc
comment to explicitly state two behaviors: passing null clears the custom
factory (resets to default), and any http.Client returned by the provided
factory is owned by the SDK and will be closed by the SDK after each download
(so callers must not return shared/long‑lived clients). Reference the public API
name configureDownloadHttpClientFactory and the underlying
ModelDownloadService.shared.configureClientFactory behavior; alternatively note
as an option that callers can return a wrapped http.Client with a no-op close if
they need to preserve ownership.

In
`@sdk/runanywhere-flutter/packages/runanywhere/test/vlm_file_resolution_test.dart`:
- Around line 6-48: Add a test that calls resolveVlmMainModelPath with a file
path (not a directory) to exercise the branch that treats file inputs by using
their parent directory; in the existing group 'VLM file resolution' create a
temp file (e.g., mainModel) in the temp dir, write contents, then pass
mainModel.path into resolveVlmMainModelPath and assert the returned path equals
mainModel.path (also assert findVlmMmprojPath when appropriate) so the code path
in resolveVlmMainModelPath that uses parent directory is covered.
🪄 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: 8049daf8-a059-4612-8539-0c2b8483b77f

📥 Commits

Reviewing files that changed from the base of the PR and between 135ef71 and 6314adf.

📒 Files selected for processing (21)
  • docs/building.md
  • docs/superpowers/plans/2026-04-17-windows-vision-restoration.md
  • docs/superpowers/specs/2026-04-17-windows-vision-design.md
  • examples/flutter/RunAnywhereAI/lib/core/services/platform_capability_service.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/pubspec.yaml
  • examples/flutter/RunAnywhereAI/test/features/vision/vlm_view_model_test.dart
  • examples/flutter/RunAnywhereAI/test/widget_test.dart
  • examples/flutter/RunAnywhereAI/windows/flutter/generated_plugin_registrant.cc
  • examples/flutter/RunAnywhereAI/windows/flutter/generated_plugins.cmake
  • sdk/runanywhere-commons/CMakeLists.txt
  • sdk/runanywhere-commons/scripts/build-windows.bat
  • sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt
  • sdk/runanywhere-flutter/packages/runanywhere/lib/internal/vlm_file_resolution.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart
  • sdk/runanywhere-flutter/packages/runanywhere/test/vlm_file_resolution_test.dart
✅ Files skipped from review due to trivial changes (6)
  • examples/flutter/RunAnywhereAI/test/widget_test.dart
  • examples/flutter/RunAnywhereAI/lib/features/vision/vision_hub_view.dart
  • sdk/runanywhere-commons/CMakeLists.txt
  • docs/building.md
  • examples/flutter/RunAnywhereAI/windows/flutter/generated_plugin_registrant.cc
  • docs/superpowers/specs/2026-04-17-windows-vision-design.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • examples/flutter/RunAnywhereAI/pubspec.yaml
  • examples/flutter/RunAnywhereAI/lib/core/services/platform_capability_service.dart
  • examples/flutter/RunAnywhereAI/windows/flutter/generated_plugins.cmake
  • sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt

CameraController(
description,
ResolutionPreset.medium,
imageFormatGroup: ImageFormatGroup.bgra8888,
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

🌐 Web query:

camera_windows supported ImageFormatGroup bgra8888

💡 Result:

No, Windows camera APIs (MediaCapture, CameraCaptureUI, MediaFrameReader) do not support an "ImageFormatGroup bgra8888". ImageFormatGroup is a Flutter camera plugin concept, not native Windows. Windows uses BitmapPixelFormat.Bgra8 (equivalent to BGRA8888) for SoftwareBitmap from camera frames, which is widely supported. CameraCaptureUI outputs JPEG/PNG files convertible to Bgra8. MediaFrameReader can request MediaEncodingSubtypes.Bgra8 for color frames.

Citations:


🏁 Script executed:

# Search for camera_windows plugin documentation and implementation
# First, check if there's any documentation or reference in the repo about camera_windows
fd -i "camera" --type f | head -20

Repository: RunanywhereAI/runanywhere-sdks

Length of output: 371


🌐 Web query:

Flutter camera_windows plugin ImageFormatGroup bgra8888 support documentation

💡 Result:

The Flutter camera_windows plugin supports the ImageFormatGroup parameter in its initializeCamera method, with a default of ImageFormatGroup.unknown. However, there is no explicit documentation confirming support for ImageFormatGroup.bgra8888 specifically on Windows. ImageFormatGroup.bgra8888 is defined in the core camera library as "32-bit BGRA" (primarily associated with iOS kCVPixelFormatType_32BGRA). Windows camera implementations (using Win32 Media Foundation or DirectShow) typically produce BGRA or similar formats natively, but the plugin docs do not specify which groups are honored or what format CameraImage uses when image streaming is enabled (recently restored via PRs but reverted; ongoing work per changelog and issues). Official pub.dev docs list image streaming as "not yet implemented" (issue #97542), though API methods like onStreamedFrameAvailable exist. No source explicitly states bgra8888 support for Windows; users report BGRA-like planes in prototypes (e.g., flutter/flutter#97542 comments). For streamed frames, check the actual CameraImage.format.group at runtime after setting imageFormatGroup: ImageFormatGroup.bgra8888 in CameraController.

Citations:


🌐 Web query:

camera_windows plugin supported image formats

💡 Result:

The camera_windows plugin, which is the Windows implementation of Flutter's camera plugin, supports JPEG as the image format for captured photos via takePicture. This is hardcoded in the plugin's C++ source code, where the constant kPictureCaptureExtension = "jpeg" is used, and photos are saved to the Pictures folder with filenames like "PhotoCapture_[timestamp].jpeg". There is a setImageFileFormat method in the API, but it is inherited from the platform interface and not implemented in camera_windows (no evidence of usage or support for other formats like PNG). Image streaming (raw CameraImage frames) is not supported (open issue #97542), but when implemented in forks, frames use BGRA8888 format.

Citations:


Monitor ImageFormatGroup.bgra8888 behavior when frame streaming is added to Windows.

Currently harmless—image streaming is not yet implemented in camera_windows (issue #97542). When added, verify that the plugin correctly handles bgra8888 format, as Windows implementations have used BGRA8888 for streamed frames in prototypes. Test at runtime to confirm the actual format group after initialization.

🤖 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 41, The code forces ImageFormatGroup.bgra8888 for camera frames; add a
runtime check after camera initialization (in the vision_camera_backend code
that sets imageFormatGroup) to detect the actual format provided by the platform
(especially on Windows/camera_windows) and log or adapt behavior if it differs;
specifically, inspect the CameraController or returned
CameraDescription/previewFormat to confirm the format group, emit a warning if
it's not bgra8888, and conditionally adjust downstream pixel handling or fall
back to a supported format to avoid misinterpreting BGRA/RGBA channel order when
Windows frame streaming is enabled.

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 | 🟡 Minor

Concurrent initializeCamera calls can leak a camera session.

There is no in-flight guard. If initializeCamera is invoked twice before the first completes (e.g. user grants permission while _initializeAsync is still running, or taps after model selection), both calls can reach _cameraBackend.createSession(...) and await nextSession.initialize(). The later assignment at line 95 overwrites _cameraSession, and the session created by the earlier call is leaked (its preview and capture handles remain open).

🛡️ Suggested in-flight guard
+  Future<void>? _initializeCameraFuture;
+
   Future<void> initializeCamera() async {
+    final existing = _initializeCameraFuture;
+    if (existing != null) return existing;
+    final completer = Completer<void>();
+    _initializeCameraFuture = completer.future;
+    try {
+      await _initializeCameraInternal();
+    } finally {
+      _initializeCameraFuture = null;
+      completer.complete();
+    }
+  }
+
+  Future<void> _initializeCameraInternal() async {
     try {
       final devices = await _cameraBackend.listDevices();
       ...
🤖 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, The initializeCamera method can be run concurrently and
leak sessions; add an in-flight guard or token to serialize/ignore overlapping
calls so only one initialization proceeds and any sessions created by earlier,
superseded calls are disposed. Concretely: introduce a private flag or
initialization token (e.g., _isInitializing or _initializeToken) checked at the
start of initializeCamera and set for the lifetime of the async operation;
before assigning _cameraSession = nextSession only allow the assignment when the
token/flag still matches (otherwise dispose nextSession); also ensure any
previous _cameraSession is awaited/disposed before creating a new session (use
await _cameraSession?.dispose()), and clear the flag/token and notify listeners
in all success/failure/early-return paths; reference initializeCamera,
_cameraSession, _isDisposed, and _safeNotifyListeners when applying these
changes.

Comment on lines +283 to 285
} catch (_) {
// Auto-stream failures stay non-blocking.
} finally {
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

Silent catch (_) hides live-mode failures.

Auto-stream errors are entirely discarded. If capture or VLM inference starts failing consistently (e.g. camera revoked, model unloaded), the UI keeps the LIVE indicator on with no feedback and no log trail to debug. Consider surfacing via the existing _error/logger or at least a debugPrint so failure modes aren't invisible.

♻️ Proposed tweak
-    } catch (_) {
-      // Auto-stream failures stay non-blocking.
+    } catch (e, st) {
+      // Auto-stream failures stay non-blocking, but surface them for debugging.
+      debugPrint('Auto-stream capture/describe failed: $e\n$st');
     }
🤖 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 283 - 285, The silent catch in the auto-stream block should surface
errors instead of swallowing them: change the catch (_) in the auto-stream
try/catch to capture the exception (e, stack) and assign a user-visible message
to the existing _error field (or call the ViewModel's error setter), and log the
error via the existing logger or debugPrint so failures (camera revoked, model
unloaded, inference errors) are recorded; also ensure any LIVE state toggles
(the code around the auto-stream start/stop functions) reflect the failure so
the UI indicator does not remain stuck on LIVE.

@peilinok peilinok closed this Apr 23, 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