Skip to content

Commit 892d84e

Browse files
sharpninjaCopilotCopilotcursoragent
authored
Release: UI improvements, deb packaging, MSIX pipeline fixes (#17)
* test: Phase 3d — mobile handler unit tests (8 handlers, 22 tests) - MobileHandlerTests.cs: tests for all 8 mobile CQRS handlers (ConnectMobileSession, DisconnectMobileSession, CreateMobileSession, TerminateMobileSession, SendMobileMessage, SendMobileAttachment, ArchiveMobileMessage, UsePromptTemplate) - Fix CS0104 ambiguity in McpRegistryPageViewModelTests: use LogicRequests alias to disambiguate DeleteMcpServerRequest vs RemoteAgent.Proto.DeleteMcpServerRequest - Fix CS8601 in ConnectMobileSessionHandler: null-coalesce host assignment (workspace.Host = host ?? "") - Fix ConnectMobileSessionHandler: remove NotifyConnectionStateChanged from catch block to prevent status overwrite on connection failure - Fix xUnit1031: make Disconnect test async, await handler result App.Tests: 67 -> 89 (+22) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: mark FR-12.12 Done, update test count to 240, mark all todos complete - requirements-completion-summary.md: FR-12.12 Pending → Done (AppLoggerProvider, InMemoryAppLogStore, AppLogViewModel, ClearAppLogHandler, SaveAppLogHandler all implemented) - requirements-completion-summary.md: TR-18.4 test count 218 → 240 (89 App.Tests + 151 Desktop.UiTests) - implementation-plan-todo.md: mark all 318 remaining items [x] (all phases 0–4 are complete) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: add session handoff 2026-02-19T00-04-44 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: add DocFX API+testing reference; add XML docs to all test classes - Split DocFX metadata into 4 entries (net10.0 src, net9.0 Desktop, net10.0 tests, net9.0 Desktop.UiTests) - Added docs/api/index.md, docs/api-tests/index.md landing pages - Added docs/filterConfig.yml (exclude System/Microsoft namespaces) - Updated docs/toc.yml with API Reference and Testing API Reference sections - Added /// <summary> + [Trait] attributes to all Desktop.UiTests handler tests (32 files) - Added /// <summary> + [Trait] to App.Tests test classes (7 files) - Added /// <summary> to SharedHandlerTestStubs.cs, InMemoryServerRegistrationStore.cs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: rebuild README; fix broken doc links - Rewrote README.md: full architecture overview, CQRS pattern, sub-VM decomposition, Management App Log, project structure, protocol tables, build scripts, test counts (240), CI/CD, F-Droid install, contributing - Fixed broken /README.md link in implementing-cli-agents.md (use GitHub URL) - Fixed broken ../SESSION_HANDOFF link in implementation-plan-mvvm-cqrs-refactor.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: fix all 505 broken DocFX breadcrumb links Add namespace stub YAMLs for intermediate namespaces that DocFX does not auto-generate (no types directly in them). Stubs added to both api/ and api-tests/ so relative breadcrumb links resolve correctly in each section. api/: RemoteAgent, RemoteAgent.App, RemoteAgent.Plugins (3 files) api-tests/: RemoteAgent, RemoteAgent.App, RemoteAgent.Desktop, RemoteAgent.Service (4 files) Site now has 422 models and 0 broken links (was 505 broken). DuplicateUids warnings (5) are non-fatal and expected for shared namespace names across api/ and api-tests/ sections. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix obsolete MAUI Page method calls in MauiPlatformServices * [WIP] Add direct FR/TR annotations for test methods (#16) * Initial plan * Add requirements traceability matrix generation script Co-authored-by: sharpninja <16146732+sharpninja@users.noreply.github.com> * Add FR/TR annotations to test classes and fix script AWK logic Co-authored-by: sharpninja <16146732+sharpninja@users.noreply.github.com> * Fix requirements matrix generation script to find all test files Co-authored-by: sharpninja <16146732+sharpninja@users.noreply.github.com> * Add requirements traceability documentation to CONTRIBUTING.md Co-authored-by: sharpninja <16146732+sharpninja@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: sharpninja <16146732+sharpninja@users.noreply.github.com> * fix: MSIX versioning, single-file self-contained packaging, and version bump script - Use SemVer (not MajorMinorPatch) from GitVersion in build-msix workflow so pre-release tags (e.g. -develop.1) appear in the MSIX filename - Set next-version to 0.1.0 in GitVersion.yml - package-msix.ps1: default SelfContained to true, add PublishSingleFile and IncludeNativeLibrariesForSelfExtract flags for single-file builds - package-msix.ps1: fall back to next-version from GitVersion.yml instead of hardcoded 1.0.0 when no -Version param, local tool, or git tag found - Add scripts/bump-minor-version.sh to increment minor version in GitVersion.yml - Desktop csproj: add RollForward=LatestPatch so installed 9.0.x patch runtime is accepted without requiring exact 9.0.0 - build-msix.ps1: display generated MSIX path on completion Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: read GitVersion.yml before git describe in version fallback git describe was finding v1.0.0-* tags and the regex was stripping the pre-release suffix, producing 1.0.0. GitVersion.yml is the authoritative source and should be checked first. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: add version source diagnostic logging to package-msix.ps1 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: print calculated version at end of package-msix.ps1 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: pause after manifest write to show version provenance and manifest content Adds a review step after AppxManifest.xml is written showing: - SemVer and MSIX Identity version used - Full manifest content Pauses for confirmation unless -Force is passed (CI always uses -Force). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: auto-detect unsigned MSIX and pass -AllowUnsigned in install script Uses Get-AuthenticodeSignature to check whether the package is signed before calling Add-AppxPackage, so unsigned dev builds install without needing manual -AllowUnsigned. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: auto-sign unsigned MSIX with dev cert instead of using -AllowUnsigned -AllowUnsigned requires Developer Mode on the machine (0x80073D2C). The install script now: - Detects unsigned packages via Get-AuthenticodeSignature - Locates signtool.exe from the Windows SDK - Creates or reuses a self-signed cert (CN=RemoteAgent Dev) - Trusts the cert in Cert:\LocalMachine\Root - Signs the MSIX in-place before calling Add-AppxPackage Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: add -Clean flag to package-msix.ps1 Runs dotnet clean on each project before publishing when -Clean is specified. Cleans service, Ollama plugin, and desktop projects according to the -ServiceOnly/-DesktopOnly flags. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: navigate to Sessions view after successful connection After StartSessionAsync succeeds, set SelectedManagementSection to 'Sessions' so the chat view is shown instead of leaving the Server Setup panel visible and overlapping the session view. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: stop service before MSIX update to avoid locked-file deployment failure Add-AppxPackage -ForceUpdateFromAnyVersion fails if RemoteAgentService is running when the update is applied. Stop the service first so the MSIX engine can replace the service binary. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: close desktop app before MSIX update to resolve 0x80073D02 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: remove --configfile from dotnet clean (unsupported flag) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: restore packages after dotnet clean before publish dotnet clean removes obj/ asset files so the implicit restore inside dotnet publish cannot resolve packages. Add explicit dotnet restore with the NuGet config after cleaning each project. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: replace dotnet clean with direct bin/obj deletion in -Clean dotnet clean cannot use --configfile so it fails to resolve package targets when globalPackagesFolder is repo-relative (.nuget/packages). Directly removing bin/ and obj/ directories is simpler and reliable. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: add -Install flag to package-msix.ps1 Calls install-remote-agent.ps1 after packaging completes. Passes -MsixPath and -CertPath (if a dev cert was generated). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: use Directory.Delete for robust bin/obj removal in -Clean Remove-Item -Recurse -Force can fail with 'directory not empty' on Windows for certain nested paths. System.IO.Directory::Delete with recursive=true is more reliable. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: add -BumpMajor/-BumpMinor/-BumpPatch flags to package-msix.ps1 Increments the corresponding semver component in GitVersion.yml next-version before the build runs, so the new version is picked up immediately by the version-detection fallback chain. The three flags are mutually exclusive. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: MsixTools PowerShell module with YAML-driven config - scripts/MsixTools/MsixTools.psm1 — reusable module with four exported functions: Read-MsixConfig, New-MsixPackage, Install-MsixPackage, Uninstall-MsixPackage - scripts/MsixTools/MsixTools.psd1 — module manifest (PS 7.0+) - msix.yml — workspace-level YAML config (package, service, desktop, plugins, build, output, icons sections) - package-msix.ps1 — now a thin wrapper; bump/validate logic stays here, build logic delegated to New-MsixPackage - install-remote-agent.ps1 — now a thin wrapper around Install-MsixPackage / Uninstall-MsixPackage Fixes 0x80073CFB: Install-MsixPackage now removes any existing package before Add-AppxPackage instead of using -ForceUpdateFromAnyVersion, which blocks same-version reinstalls with changed contents. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: resolve OutDir to absolute path before icon generation System.Drawing.Bitmap.Save() is a .NET method that ignores PowerShell's working directory and fails with relative paths. Resolving OutDir to an absolute path early ensures all derived paths (msix-layout/Assets/*) are also absolute. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: resolve WorkspaceRoot to absolute path at function entry [System.IO.Path]::GetFullPath() resolves relative to .NET's CWD which can differ from PowerShell's $PWD. Using PS's own session-state path resolver ensures WorkspaceRoot (and all derived paths: OutDir, project paths, icons) are always absolute, fixing Bitmap.Save failure when -WorkspaceRoot . is passed. After pulling, reload the module before calling directly: Import-Module .\scripts\MsixTools\MsixTools.psd1 -Force Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: use Convert-Path to establish absolute WorkspaceRoot Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: add -NoBuild flag; make -Clean and -NoBuild mutually exclusive -NoBuild skips dotnet publish and uses existing artifacts/publish-* output. Fails fast with a clear error if expected output is missing. -Clean and -NoBuild are validated as mutually exclusive in both the module and the wrapper script. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: remove inline MsixTools (replaced by submodule) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: add MsixTools as git submodule https://github.com/sharpninja/MsixTools Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor: wrapper scripts are now zero-logic param forwarders All version-bump validation and logic moved into New-MsixPackage in the MsixTools module. Wrapper scripts contain only param declarations and a single module function call. Submodule updated to latest. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: update MsixTools submodule to include bump params Points to commit with -BumpMajor/-BumpMinor/-BumpPatch added to New-MsixPackage; all version logic now lives in the module. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: update MsixTools submodule (add MCP server) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use Segoe MDL2 Assets for icons and Segoe UI as base font on Windows On Windows, resolve IconFontFamily to 'Segoe MDL2 Assets' and AppFontFamily to 'Segoe UI' at app startup. Falls back to FontFamily.Default on other platforms. - App.axaml.cs: set font resources after XAML load based on OS - MainWindow.axaml: apply AppFontFamily to Window, add fa|FontIcon style with IconFontFamily Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Refactor copy-status-log to follow CQRS pattern with tests - Add IClipboardService abstraction and AvaloniaClipboardService impl - Add CopyStatusLogRequest + CopyStatusLogHandler (formats entries oldest-first as markdown with yyyy-MM-dd HH:mm:ss timestamps) - Register IClipboardService and CopyStatusLogHandler in DI - Refactor MainWindowViewModel: remove direct clipboard/StringBuilder code; dispatch CopyStatusLogRequest via IRequestDispatcher - Add NullClipboardService and CapturingClipboardService stubs - Add CopyStatusLogHandlerTests (7 tests; all passing) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add directive: UI buttons require CQRS handler + complete tests Document the rule in both docs/REPOSITORY_RULES.md (canonical) and .github/copilot-instructions.md (agent-facing summary): - Every Desktop UI button must be backed by a *Request/*Handler pair - Handler dispatched via IRequestDispatcher in the ViewModel - *HandlerTests.cs covering success, failure, and edge cases required - Null* stubs for new infrastructure interfaces in SharedHandlerTestStubs - CopyStatusLogHandler cited as reference implementation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add Open Logs Folder button to App Log section (CQRS + tests) - IFolderOpenerService + SystemFolderOpenerService (cross-platform) - OpenLogsFolderRequest + OpenLogsFolderHandler (validates path exists) - AppLogViewModel: logsFolder param, OpenLogsFolderCommand - App.axaml.cs: register IFolderOpenerService, OpenLogsFolderHandler, AppLogViewModel factory (passes dataDir) - MainWindow.axaml: Open Logs Folder button in App Log section - 6 handler tests; NullFolderOpenerService / CapturingFolderOpenerService stubs - SharedHandlerTestStubs.CreateAppLog updated with default logsFolder param Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Extract navigation panels to UserControls with IsVisible bindings - 13 panel UserControls in src/RemoteAgent.Desktop/Views/Panels/: ServerSetupPanel, SessionsPanel, LocalServerPanel, OpenSessionsPanel, StructuredLogsPanel, SecurityPanel, HistoryPanel, AuthUsersPanel, PluginsPanel, McpPanel, PromptsPanel, AppLogPanel, SettingsPanel - MainWindow.axaml: add panels:* namespace; replace ContentPresenter with a Grid hosting all panels bound via Is*SectionSelected (IsVisible). All panels are always in the logical tree — no dynamic switching needed. - Add FindControlDeep<T> extension (TestHelpers/ControlExtensions.cs) that traverses the full logical tree across UserControl NameScope boundaries. - Update MainWindowUiTests to use FindControlDeep for panel-scoped controls. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix Windows port: bind to 5243 (not 5244) on Windows appsettings.json PlatformUrls:Windows was set to 5244, but all clients (LocalServerManager, ServerRegistration default, MainWindowViewModel edit port, mobile ConnectMobileSessionHandler / UsePromptTemplateHandler, and the pairing QR-code deep link) hard-code 5243 as the default port. - appsettings.json: Windows -> http://0.0.0.0:5243 - Program.cs /pair/key: remove Windows-specific 5244 fallback; use 5243 unconditionally for non-HTTPS when the port is absent from the Host header. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix Windows port binding: use ConfigureKestrel instead of UseUrls Root cause: launchSettings.json applicationUrl: "http://0.0.0.0:5243" is converted to ASPNETCORE_URLS by dotnet run (including run-service-local.ps1). ASPNETCORE_URLS has higher priority than UseUrls(), so the Windows-specific port 5244 was silently overridden to 5243. Fix: replace UseUrls() with WebHost.ConfigureKestrel() + ListenAnyIP(). An explicit Kestrel Listen* call has the highest priority in ASP.NET Core's URL resolution chain and is not overridable by ASPNETCORE_URLS. Also: - Restore appsettings.json PlatformUrls:Windows back to 5244 - Restore /pair/key pairing fallback port (Windows=5244, Linux=5243) - Add Http1AndHttp2 protocol constraint on the explicit endpoint (was previously set via Kestrel:EndpointDefaults in appsettings.json, which would be ignored once an explicit endpoint is registered) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add Open Logs Folder button to status log viewer; icon-only buttons - Status log header: add Open Logs Folder button (AppLog.OpenLogsFolderCommand, x:Name=StatusLogOpenLogsFolderButton, folder-open glyph &#xED25;) - Status log header: Close button now icon-only (&#xE711;), no text label - Status log header: Copy as Markdown button now icon-only (&#xE8C8;), no text - AppLogPanel: Open Logs Folder button now icon-only (&#xED25;), no text - MainWindowUiTests: assert StatusLogOpenLogsFolderButton is present Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix Build MSIX: upgrade gittools/actions to v3 for GitVersion 6.x gittools/actions/gitversion/setup@v1 only accepts versions >=5.2.0 <6.1.0. The versionSpec '6.x' resolves to 6.6.0, which falls outside that range. v3 of the action supports GitVersion 6.x. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add .deb build job and GitHub Release to pipeline - Add build-deb job (ubuntu-latest): checkout, .NET 10+9, GitVersion v3/6.x, runs scripts/package-deb.sh, uploads remote-agent-deb-* artifact - Expose SemVer + MajorMinorPatch outputs from both build jobs - Add release job (push-only, needs both build jobs): - Downloads MSIX and .deb artifacts - Creates GitHub Release with gh CLI (--prerelease on non-main branches) - Idempotent: deletes existing release for the tag before recreating - Attaches *.msix, *.deb, and *.cer (if signed) as release assets - Add scripts/package-deb.sh to path triggers Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: commit working tree changes before PR - GitVersion.yml: bump next-version to 0.1.3 - Program.cs: log actual listening URLs on startup - package-deb.sh: add remote-agent-ctl management script installation - docs/faq.md: update docs - scripts/bump-patch-version.sh, monitor-service-windows.ps1, remote-agent-ctl.sh: add untracked scripts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: expose DbPath on ILocalStorage; log connection details on open - ILocalStorage: add DbPath property (exposes the LiteDB file path) - LiteDbLocalStorage: implement DbPath => _dbPath - AgentGatewayService: log connection_opened event with session log, structured log, and database paths for diagnostics Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: add Windows Event Log as ILogger provider - ConfigureWindowsEventLog() annotated [SupportedOSPlatform("windows")] calls builder.Logging.AddEventLog with SourceName = "Remote Agent Service" - Called from Main() behind OperatingSystem.IsWindows() guard - appsettings.json: add EventLog log-level section (Information+ for default, Warning for Microsoft.AspNetCore) so all ILogger calls appear in Event Viewer Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: versioned Windows service display name - msix.yml: add service.displayName = 'Remote Agent Service' - MsixTools submodule: combined MSIX packages now generate a dedicated Application entry for the service so services.msc shows 'Remote Agent Service <SemVer>' instead of the desktop app name Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: session handoff 2026-02-19T18-35-50-0600 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: checkout submodules in build-msix CI job MsixTools is a git submodule (scripts/MsixTools/). Without submodules: recursive in the checkout step the directory is empty and Import-Module fails with 'no valid module file found'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: use Http2 (h2c) for cleartext gRPC endpoint Http1AndHttp2 over cleartext requires TLS-based ALPN to negotiate the protocol version, producing: 'HTTP/2 is not enabled... TLS is not enabled. HTTP/2 requires TLS application protocol negotiation. Connections will use HTTP/1.1.' This is a local gRPC service without TLS; the correct protocol is Http2 (h2c — HTTP/2 cleartext). All gRPC clients already use GrpcChannelOptions with no TLS (Grpc.Net.Client default for http://). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: replace port Entry with Picker on mobile; default to 5244 - MainPageViewModel: DefaultPort changed to "5244" (Windows service port); add static AvailablePorts = ["5244", "5243"] for Picker ItemsSource - MainPage.xaml: xmlns:vm added; both port Entry controls replaced with Picker bound to MainPageViewModel.AvailablePorts / Port (connect card + persistent top-bar; WidthRequest=90 on the bar picker) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: centralize service port in ServiceDefaults; enforce port picker with tests - Add ServiceDefaults.cs (platform-aware: 5244 Windows, 5243 Linux) - Replace hardcoded 5243 literals in all desktop ViewModels and infrastructure - Add PortPickerViewModelTests.cs (11 unit tests) enforcing: AvailablePorts = ["5244", "5243"], default Port = "5244" - Expose MobileHandlerTests.CreateDefaultViewModel() (internal) for reuse - Update MobileConnectionUiTests.cs: fix default port assertion (5243→5244), add Picker class check, add drop-down items verification Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: use Http1AndHttp2 + h2c switch to allow gRPC and REST on same port Service: revert Kestrel from Http2-only to Http1AndHttp2 so REST endpoints (/api/sessions/capacity, /pair/key, etc.) continue to accept HTTP/1.1 while gRPC clients use HTTP/2 cleartext (h2c prior knowledge). Desktop + Mobile: set AppContext switch System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport = true before any gRPC channel is created, enabling h2c connections to the Http1AndHttp2 Kestrel endpoint without TLS. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: set Kestrel EndpointDefaults to Http1AndHttp2 in appsettings.json The config key overrode the Program.cs ConfigureKestrel call, keeping the endpoint on Http2-only and causing HTTP_1_1_REQUIRED errors on gRPC connects. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: migrate 13 management REST endpoints to gRPC; revert to Http2-only Add 13 RPCs to AgentGateway.proto and implement in AgentGatewayService: CheckSessionCapacity, ListOpenSessions, ListAbandonedSessions, TerminateSession, ListConnectedPeers, ListConnectionHistory, ListBannedPeers, BanPeer, UnbanPeer, ListAuthUsers, ListPermissionRoles, UpsertAuthUser, DeleteAuthUser Rewrite ServerCapacityClient to use GrpcChannel for all 13 calls. Remove the 13 REST MapGet/Post/Delete registrations from Program.cs. Regenerate AgentGateway.cs + AgentGatewayGrpc.cs from proto. Revert to Http2-only (appsettings.json + Program.cs). Remove AppContext.SetSwitch h2c lines from Desktop and Mobile. Pairing endpoints (/, /pair, /pair/key) remain as REST on Http1AndHttp2 is no longer needed since the desktop management client is now pure gRPC. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: use shared app logo in desktop window and MSIX Generate appicon.png (256/48/32/16px) from the same SVG source used by the mobile app (appicon.svg is identical in both projects). - Register all four sizes as AvaloniaResource in RemoteAgent.Desktop.csproj - Set Icon="avares://RemoteAgent.Desktop/Assets/AppIcon/appicon.png" on MainWindow so the taskbar, Alt-Tab, and title bar all show the logo - msix.yml already references appicon.svg (same as mobile) — no change needed Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: end server session when closing a tab TerminateDesktopSessionHandler now calls TerminateSessionAsync on the server (via IServerCapacityClient gRPC) before disconnecting locally, so the server immediately cleans up the session rather than waiting for an idle/timeout. - Inject IServerCapacityClient into TerminateDesktopSessionHandler - Call TerminateSessionAsync (best-effort) when session.IsConnected and workspace has a valid port; local cleanup always proceeds even if the server call fails - Add LastTerminatedSessionId tracking to StubCapacityClient - Add two new tests: server RPC is invoked, server failure still removes locally (166 tests total, all passing) - Fix stale TerminateDesktopSessionHandler() instantiation in MainWindowUiTests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: wire MessageReceived/ConnectionStateChanged before connecting session CreateDesktopSessionHandler was calling session.SessionClient.ConnectAsync without first subscribing to MessageReceived and ConnectionStateChanged. Messages from the server arrived in the ReceiveLoop and were silently dropped because no handler was registered. Root cause: ConnectSessionAsync (the only method that did the wiring) was unreachable dead code — NewSessionAsync dispatched to the CQRS handler which bypassed it entirely. Fix: - Extract event registration into ServerWorkspaceViewModel.RegisterSessionEvents() and cleanup into UnregisterSessionEvents() - ConnectSessionAsync delegates to RegisterSessionEvents (dead code path is now consistent) - CreateDesktopSessionHandler calls RegisterSessionEvents BEFORE ConnectAsync so no early server messages (e.g. SessionStarted event) are lost - TerminateDesktopSessionHandler calls UnregisterSessionEvents on teardown Tests: - FakeAgentSession.MessageReceived is now a real subscribable event - FakeAgentSession.SimulateMessage fires MessageReceived to test wiring - StubSessionFactory.LastFakeSession exposes the last created session client - New test: HandleAsync_ShouldRegisterSessionEventsBeforeConnect verifies that a synthetic server message reaches session.Messages (167 tests pass) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: session handoff 2026-02-19T21-06-54-0600 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: bump next-version to 0.1.12 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor: serialise Connect writes via Channel<ServerMessage>; remove dead code - Replace all direct responseStream.WriteAsync calls inside requestTask and StreamReaderToResponse with Channel<ServerMessage>.Writer.TryWrite - Add single drain loop (Task.Run) as the sole writer to responseStream; cancels cts on write failure to trigger clean disconnect cleanup - Change StreamReaderToResponse signature to accept ChannelWriter<ServerMessage> instead of IServerStreamWriter<ServerMessage> - Wrap stdout/stderr/requestTask awaiting in try/finally so outChannel is always completed even on unexpected exceptions - Remove ConnectSessionAsync dead code from ServerWorkspaceViewModel; its logic lives in RegisterSessionEvents + CreateDesktopSessionHandler Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: add camera QR scan pairing flow with ZXing.Net.Maui and Android deep link support - Add IQrCodeScanner and IDeepLinkService interfaces to App.Logic - Add ScanQrCodeRequest + ScanQrCodeHandler (camera or deep-link bypass) * dual-mode: invokes ZXing camera scanner or parses RawUri directly * saves host/port/apiKey to IAppPreferences on success * internal static ParseAndApply + ParseQuery for full unit testability - Add QrScannerPage (ZXing CameraBarcodeReaderView) + code-behind (TCS pattern) - Add MauiQrCodeScanner: requests CAMERA permission, pushes QrScannerPage modally - Add DeepLinkService: thread-safe singleton, queues URI if no subscriber yet - Wire .UseBarcodeReader() in MauiProgram; register IDeepLinkService, IQrCodeScanner - Add CAMERA permission + uses-feature to AndroidManifest.xml - Add [IntentFilter] for remoteagent://pair scheme on MainActivity; dispatch via IDeepLinkService - Add ScanQrCode button to MainPage.xaml connection view - Add ApiKey + PrefApiKey + ScanQrCodeCommand + deep-link subscription to MainPageViewModel - Pass workspace.ApiKey in ConnectMobileSessionHandler.ConnectAsync - Add 7 ScanQrCodeHandlerTests covering all handler paths - Add NullDeepLinkService stub + update CreateWorkspace in MobileHandlerTests All 108 App.Tests + 167 Desktop.UiTests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: replace obsolete Frame with Border in QrScannerPage; fix nullable sender in event handlers Frame is obsolete as of .NET 9 — replaced with Border using StrokeShape=RoundRectangle. Event handler sender parameters changed to object? to match EventHandler<T> delegate signatures (TreatWarningsAsErrors=true treats CS0618/CS8622 as errors). All 108 App.Tests + 27 Service.Tests + 167 Desktop.UiTests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix MSIX CI: exit 0 to prevent LASTEXITCODE leak from native commands After New-MsixPackage runs dotnet/makeappx/signtool, the $LASTEXITCODE from the last native command can be non-zero even on success. GitHub Actions' pwsh runner checks $LASTEXITCODE after the script exits, so adding explicit 'exit 0' prevents false failure reports. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Merge Build MSIX workflow into Build and Deploy pipeline Move build-msix and build-deb jobs from the standalone build-msix.yml into build-deploy.yml so all packaging happens in one unified pipeline. - Add build-msix job (windows-latest, .NET 10+9, package-msix.ps1) - Add build-deb job (ubuntu-latest, .NET 10+9, package-deb.sh) - Both jobs depend on detect + build, sharing the semver output - release-beta now depends on build-msix and build-deb, downloads MSIX and DEB artifacts, and includes them in the beta release - Delete the now-redundant build-msix.yml workflow Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: serve /pair web UI on dedicated HTTP/1+2 port (1+gRPC port) Kestrel now listens on two ports: - Primary gRPC port (5244/5243): HTTP/2 only, for gRPC traffic - Web pairing port (15244/15243): HTTP/1+2, for browser /pair flow /pair, POST /pair, and /pair/key endpoints are restricted to the web port via RequireHost so no HTTP/1.1 traffic can reach the gRPC listener. webPort=0 default keeps integration tests unaffected. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: SetPairingUsers — PS script + Desktop toolbar + gRPC RPC Part 1: Add scripts/set-pairing-user.ps1 - Accepts -Username, -Password, -Replace params - Finds appsettings.json via SC query / default path / repo fallback - Computes SHA-256 password hash; upserts or replaces PairingUsers array - Restarts RemoteAgentService (non-fatal if not found) Part 2: Full-stack SetPairingUsers feature - Proto: add SetPairingUsers RPC + PairingUserEntry/Request/Response messages - Service: override SetPairingUsers in AgentGatewayService (reads/writes appsettings.json) - Desktop client: add SetPairingUsersAsync to IServerCapacityClient + ServerCapacityClient - Dialog: IPairingUserDialog, PairingUserDialog.axaml, PairingUserDialogViewModel, AvaloniaPairingUserDialog - CQRS: SetPairingUserRequest + SetPairingUserHandler - ViewModels: HasConnectedSession on ServerWorkspaceViewModel; SetPairingUserCommand on MainWindowViewModel - UI: SetPairingUserButton in MainWindow toolbar (enabled when HasConnectedSession) - DI: register SetPairingUserHandler + IPairingUserDialog in App.axaml.cs - Tests: SetPairingUserHandlerTests (5 tests); stubs updated in SharedHandlerTestStubs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: use IOptionsMonitor so /pair reloads after SetPairingUsers writes appsettings.json IOptions<AgentOptions> is a startup snapshot; switching to IOptionsMonitor<AgentOptions> means options.CurrentValue reflects the file that SetPairingUsers just wrote, without requiring a service restart. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Feat: generate and return API key when saving pairing user SetPairingUsers RPC now generates a cryptographically random 32-byte API key, saves it as Agent.ApiKey in appsettings.json alongside the pairing user, and returns it in SetPairingUsersResponse.GeneratedApiKey. The desktop handler applies the returned key to Workspace.ApiKey so subsequent gRPC calls (capacity check, session creation) use it immediately without requiring a manual reconnect. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: forward ApiKey to server info and capacity checks in ConnectMobileSessionHandler Without this, a service configured with Agent:ApiKey returns 401 on GetServerInfoAsync and GetSessionCapacityAsync, causing the mobile app to show 'Could not verify server session capacity'. Added regression test that asserts the key reaches both API calls. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: use Shell.Current.Navigation + MainThread for QR scanner modal push page.Navigation.PushModalAsync on a ContentPage inside a MAUI Shell can silently no-op on Android when the page isn't the active navigation host. Switch to Shell.Current.Navigation which always routes through the Shell's own navigation controller. Also wrap in MainThread.InvokeOnMainThreadAsync because Permissions.RequestAsync may resume on a thread-pool thread. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * UX: SelectableTextBlock everywhere in Avalonia; simplify mobile login UI; persist ApiKey on connect - Replace all TextBlock with SelectableTextBlock in all 16 Avalonia .axaml files so users can copy text from any label in the desktop app. - Simplify mobile connection card to two buttons: * 'Scan QR to Pair' — disabled when ApiKey is already stored * 'Connect' — disabled until ApiKey is stored (requires QR scan first) Remove New Session / Terminate Session / extra Connect buttons from pre-connect view. - Add HasApiKey property to MainPageViewModel; update CanExecute for both commands. - ConnectMobileSessionHandler now persists ApiKey alongside Host/Port on successful connect so server details survive app restart as a complete set. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Style: global 4px margin on all interactive/display controls in Avalonia app Single style rule in App.axaml targeting Button, TextBox, ComboBox, CheckBox, ListBox, SelectableTextBlock, and TabControl so every control gets consistent 4px spacing without touching individual XAML files. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix: generate QR code server-side (embedded PNG) instead of CDN JS The CDN qrcode.js script was blocked/unavailable so no QR was shown. Switch to QRCoder (server-side) which generates the QR as a PNG and embeds it as a data:image/png;base64 <img> tag — no external requests needed, works in air-gapped / firewalled environments. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Replace QR camera scanner with server login webview for pairing - Rename 'Scan QR to Pair' button to 'Login' - Host setter calls ChangeCanExecute on ScanQrCodeCommand - IQrCodeScanner.ScanAsync() now accepts loginUrl parameter - ScanQrCodeHandler builds http://{host}:1{port}/pair URL and passes to scanner; validates host not empty - MauiQrCodeScanner opens PairLoginPage (WebView) instead of camera; no camera permission needed - PairLoginPage: WebView loads /pair login form; on navigate to /pair/key, JS extracts deep link from <a class='btn'> href; closes modal with result - Remove QrScannerPage (camera scanner) and ZXing.Net.Maui.Controls dependency - Add tests: HandleAsync_NoHost_ReturnsFail, HandleAsync_BuildsLoginUrlFromHostPort - All 138 tests pass (111 App + 27 Service) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: update session handoff 2026-02-20 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: add FR-17/18 and TR-19/20 for device pairing, API key management, and desktop UX - FR-17: Device pairing flow (admin sets credentials, service generates API key, web login, deep link extraction) - FR-18: Desktop UX refinements (SelectableTextBlock, 4px margin) - TR-19: Pairing infrastructure (SetPairingUsers RPC, IOptionsMonitor, dual-port, QRCoder, PairLoginPage WebView, deep-link format) - TR-20: Desktop UX technical (SelectableTextBlock replacement, global style) - Updated completion summary: 80 FR + 124 TR, all Done Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Hardcode server connection mode on Android, remove mode selector dialog Remove IConnectionModeSelector interface, MauiConnectionModeSelector class, and all direct-mode code paths. Android always connects in server mode. Remove ConnectionModeLabel from UI and associated ViewModel property. Update all tests to reflect removal of mode selection. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Migrate GetSessionCapacityAsync to gRPC, remove dead HTTP helpers Replace REST call to /api/sessions/capacity with gRPC CheckSessionCapacity. Remove unused CreateHttpFailure, TryReadErrorDetailAsync, JsonOptions, and System.Net.Http.Json / System.Text.Json imports. All ServerApiClient methods now use gRPC exclusively. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Hide navigation until connection and session are established Add IsConnected and ConnectionStateChanged to ISessionCommandBus. AppShellViewModel exposes IsConnected, subscribes to state changes. Flyout items (sessions, Start Session, Settings, Account) are hidden until connected. MCP Registry tab hidden until connected. Settings and Account pages also hidden from tab bar until connected. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Hide connection card in chat view when already connected The host/port/connect/disconnect card in the chat workspace is now hidden once connected, giving more space to the chat messages. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add server profiles: save connection details per host:port - Create ServerProfile model + IServerProfileStore in App.Logic (shared) - LiteDB-backed LocalServerProfileStore for mobile (CRUD by host:port) - ConnectMobileSessionHandler auto-saves profile on successful connect - SettingsPage rewritten with server profile list + edit form - SettingsPageViewModel with save/delete commands - Desktop ServerRegistration gains PerRequestContext + DefaultSessionContext - Desktop ServerSetupPanel shows per-request/session context fields - SaveServerRegistrationRequest/Handler persist new fields - ServerWorkspaceViewModel loads PerRequestContext from registration - All 310 tests pass (110 App + 27 Service + 173 Desktop) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Make Enter key send message on Android Android has no Ctrl+Enter, so intercept the hardware Enter key on the native AppCompatEditText to dispatch SendMessageCommand immediately. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add FR-19/20, TR-21/22: server profiles and mobile chat UX requirements - FR-19: Server profiles with persistent connection settings per host:port - FR-20: Mobile chat UX (Enter sends, server-only mode, nav hidden until connected, gRPC-only) - TR-21: Implementation details for server profile storage (LiteDB, shared model, auto-save) - TR-22: Implementation details for mobile Enter key, connection mode, visibility, gRPC migration - Updated FR-2.5/2.6 and TR-5.7 to reflect platform-specific Enter behavior - Updated requirements-test-coverage.md with FR-17..20 and TR-19..22 entries Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix Android Enter-to-send, icon-only Send button, compact button padding - Use EditorAction + ImeAction.Send instead of KeyPress (soft keyboard does not fire KeyPress events on Android) - Send button now uses FontImageSource icon instead of text - Reduced global Button padding from 24,12 to 12,6 and min height from 48 to 36 for a more compact mobile UI Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * CQRS server profile handlers + clear API key feature - Add SaveServerProfileRequest/Handler for persisting profile edits - Add DeleteServerProfileRequest/Handler for removing profiles - Add ClearServerApiKeyRequest/Handler to clear stored API keys - Refactor SettingsPageViewModel to dispatch all commands via IRequestDispatcher - Add API key status display and Clear API Key button to SettingsPage - Register all three handlers in MauiProgram.cs DI - Add ServerProfileHandlerTests with 7 test cases (save, delete, clear key) - Compact mobile chat UI: remove icon label, shrink session tabs/buttons - Reduce toolbar to Connect/Disconnect only (session controls in card) - Default port changed to 5243 (Linux/Docker) - Update PortPickerViewModelTests to match new default Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Replace .NET splash screen logo with app icon Replaced the default .NET text logo (splash.svg) with the app's own icon foreground (appiconfg.svg) so the Android splash screen shows the Remote Agent logo instead of the generic .NET branding. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix Android soft keyboard Enter: show Send action instead of newline SetRawInputType(InputTypes.ClassText) overrides the IME input type so Android shows a Send button instead of Enter/newline for the multi-line Editor control. The EditorAction handler fires on tap. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: update session handoff 2026-02-20T05:22, remove stale handoff Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add file transfer, agents panel, connection settings, toolbar TextBlock fix, UpdateAgentRunner error logging * Add mandatory session logging priority order to copilot-instructions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add mandatory session logging to Cursor, Codex, and Cline agent configs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: Remove redundant steps from copilot-instructions priority order Steps 1-2 (Read AGENTS-README-FIRST.yaml and GET /health) are already handled by the AGENTS-README-FIRST.yaml marker file processing and module bootstrap. Removing to match FunWasHad workspace. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: remove redundant session logging preamble and fix old /mcp/ routes Session procedures now provided by AGENTS-README-FIRST.yaml marker file. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Make requirements matrix awk parser mawk-compatible Co-authored-by: Payton Byrd <sharpninja@users.noreply.github.com> * docs: switch session-log instructions from entry to turn --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Payton Byrd <sharpninja@users.noreply.github.com>
1 parent 3463576 commit 892d84e

183 files changed

Lines changed: 18868 additions & 2991 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.clinerules

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Cline Rules
2+
3+
## ⚠️ PRIORITY ORDER — NON-NEGOTIABLE ⚠️
4+
5+
**Speed is never more important than following workspace procedures.**
6+
7+
Before doing ANY work on ANY user request, you MUST complete these steps in order:
8+
9+
1. **Read `AGENTS-README-FIRST.yaml`** in the repo root for the current API key and endpoints
10+
2. **GET `/health`** to verify the MCP server is running
11+
3. **POST `/mcp/sessionlog`** with your session entry — do NOT proceed until this succeeds
12+
4. **GET `/mcp/sessionlog?limit=5`** to review recent session history for context
13+
5. **GET `/mcp/todo`** to check current tasks
14+
6. **THEN** begin working on the user's request
15+
16+
On EVERY subsequent user message:
17+
1. POST an updated session log entry BEFORE starting work
18+
2. Complete the user's request
19+
3. POST the final session log with results, actions taken, and files modified
20+
21+
**If you skip any of these steps, STOP and go back and do them before continuing.**
22+
Session logging is not optional, not deferred, and not secondary to the task.
23+
Failure to maintain the session log is a compliance violation.

.github/copilot-instructions.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# GitHub Copilot Instructions for Remote Agent
22

3+
For specific operational instructions (session bootstrap, session log turn workflow, and helper command sequence), follow `AGENTS-README-FIRST.yaml`.
4+
35
## Development Branch Strategy
46

57
**IMPORTANT: Always work in the `develop` branch for all development tasks.**
@@ -22,6 +24,17 @@
2224

2325
## Code Quality Standards
2426

27+
### UI Buttons — CQRS Required
28+
29+
**Every button added to the Desktop UI must be backed by a CQRS request/handler pair with complete tests.**
30+
31+
- Create `*Request` in `src/RemoteAgent.Desktop/Requests/` and `*Handler` in `src/RemoteAgent.Desktop/Handlers/`
32+
- Register the handler as a transient in `App.axaml.cs` `ConfigureServices`
33+
- The ViewModel command **must dispatch via `IRequestDispatcher`** — no handler logic in the ViewModel
34+
- Add `*HandlerTests.cs` in `tests/RemoteAgent.Desktop.UiTests/Handlers/` covering success, failure, and edge cases
35+
- Add a `Null*` stub for any new infrastructure interface to `SharedHandlerTestStubs.cs`
36+
- See `docs/REPOSITORY_RULES.md` for full details and the `CopyStatusLogHandler` as a reference implementation
37+
2538
### Warnings as Errors
2639

2740
- All warnings are treated as errors (see `Directory.Build.props`)
@@ -101,3 +114,5 @@ Before completing any task:
101114
**Sign commits**
102115
**Run tests before completing tasks**
103116
**Use bash only in workflows (no Python)**
117+
**Every UI button → CQRS request/handler + complete tests**
118+

.github/workflows/build-deploy.yml

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,79 @@ jobs:
197197
dotnet restore RemoteAgent.Desktop.csproj --configfile ../../NuGet.Config -v minimal -p:TargetFramework=net9.0 -p:TargetFrameworks=net9.0
198198
dotnet build RemoteAgent.Desktop.csproj -c Release --no-restore -v minimal -p:TargetFramework=net9.0 -p:TargetFrameworks=net9.0
199199
200+
build-msix:
201+
name: Build MSIX (Windows)
202+
runs-on: windows-latest
203+
needs: [detect, build]
204+
if: needs.detect.outputs.code == 'true'
205+
steps:
206+
- uses: actions/checkout@v4
207+
with:
208+
fetch-depth: 0
209+
submodules: recursive
210+
211+
- name: Setup .NET 10
212+
uses: actions/setup-dotnet@v4
213+
with:
214+
dotnet-version: '10.0.x'
215+
216+
- name: Setup .NET 9 (desktop)
217+
uses: actions/setup-dotnet@v4
218+
with:
219+
dotnet-version: '9.0.x'
220+
221+
- name: Build MSIX package
222+
shell: pwsh
223+
env:
224+
VERSION: ${{ needs.build.outputs.semver }}
225+
run: |
226+
.\scripts\package-msix.ps1 -Version $env:VERSION -Force
227+
228+
- name: Upload MSIX artifact
229+
uses: actions/upload-artifact@v4
230+
with:
231+
name: msix-${{ needs.build.outputs.major_minor_patch }}
232+
path: |
233+
artifacts/*.msix
234+
artifacts/*.cer
235+
if-no-files-found: error
236+
retention-days: 30
237+
238+
build-deb:
239+
name: Build .deb (Linux)
240+
runs-on: ubuntu-latest
241+
needs: [detect, build]
242+
if: needs.detect.outputs.code == 'true'
243+
steps:
244+
- uses: actions/checkout@v4
245+
with:
246+
fetch-depth: 0
247+
248+
- name: Setup .NET 10
249+
uses: actions/setup-dotnet@v4
250+
with:
251+
dotnet-version: '10.0.x'
252+
253+
- name: Setup .NET 9 (desktop)
254+
uses: actions/setup-dotnet@v4
255+
with:
256+
dotnet-version: '9.0.x'
257+
258+
- name: Build .deb packages
259+
env:
260+
VERSION: ${{ needs.build.outputs.semver }}
261+
run: |
262+
chmod +x scripts/package-deb.sh
263+
./scripts/package-deb.sh --version "$VERSION"
264+
265+
- name: Upload .deb artifacts
266+
uses: actions/upload-artifact@v4
267+
with:
268+
name: deb-${{ needs.build.outputs.major_minor_patch }}
269+
path: artifacts/*.deb
270+
if-no-files-found: error
271+
retention-days: 30
272+
200273
android:
201274
name: Build Android APK
202275
runs-on: ubuntu-latest
@@ -273,7 +346,7 @@ jobs:
273346
release-beta:
274347
name: Create beta release
275348
runs-on: ubuntu-latest
276-
needs: [build, desktop-build, android, docker]
349+
needs: [build, desktop-build, android, docker, build-msix, build-deb]
277350
permissions:
278351
contents: write
279352
steps:
@@ -313,14 +386,30 @@ jobs:
313386
with:
314387
name: android-apk
315388

389+
- name: Download MSIX artifacts
390+
uses: actions/download-artifact@v4
391+
with:
392+
name: msix-${{ needs.build.outputs.major_minor_patch }}
393+
path: release-assets/msix
394+
395+
- name: Download .deb artifacts
396+
uses: actions/download-artifact@v4
397+
with:
398+
name: deb-${{ needs.build.outputs.major_minor_patch }}
399+
path: release-assets/deb
400+
316401
- name: Create beta release
317402
uses: softprops/action-gh-release@v2
318403
with:
319404
tag_name: v${{ steps.version.outputs.version }}-beta.${{ github.run_number }}
320405
name: Beta v${{ steps.version.outputs.version }} (build ${{ github.run_number }})
321406
prerelease: true
322407
body_path: release-notes.md
323-
files: com.companyname.remoteagent.app-Signed.apk
408+
files: |
409+
com.companyname.remoteagent.app-Signed.apk
410+
release-assets/msix/*.msix
411+
release-assets/msix/*.cer
412+
release-assets/deb/*.deb
324413
325414
docker:
326415
name: Build and push Docker image

.github/workflows/build-msix.yml

Lines changed: 0 additions & 117 deletions
This file was deleted.

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "scripts/MsixTools"]
2+
path = scripts/MsixTools
3+
url = https://github.com/sharpninja/MsixTools.git

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
"path": "pwsh.exe",
66
"args": ["-NoProfile"]
77
}
8-
}
8+
},
9+
"fusion-360-helper.enabled": false
910
}

AGENTS.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,27 @@
11
# Repository Guidelines
22

3+
## ⚠️ PRIORITY ORDER — NON-NEGOTIABLE ⚠️
4+
5+
**Speed is never more important than following workspace procedures.**
6+
7+
Before doing ANY work on ANY user request, you MUST complete these steps in order:
8+
9+
1. **Read `AGENTS-README-FIRST.yaml`** in the repo root for the current API key and endpoints
10+
2. **GET `/health`** to verify the MCP server is running
11+
3. **POST `/mcp/sessionlog`** with your session turn — do NOT proceed until this succeeds
12+
4. **GET `/mcp/sessionlog?limit=5`** to review recent session history for context
13+
5. **GET `/mcp/todo`** to check current tasks
14+
6. **THEN** begin working on the user's request
15+
16+
On EVERY subsequent user message:
17+
1. Post a new session log turn (`Add-McpSessionTurn`) before starting work.
18+
2. Complete the user's request.
19+
3. Update the turn with results (`Response`) and actions (`Add-McpAction`) when done.
20+
21+
**If you skip any of these steps, STOP and go back and do them before continuing.**
22+
Session logging is not optional, not deferred, and not secondary to the task.
23+
Failure to maintain the session log is a compliance violation.
24+
325
## Project Structure & Module Organization
426
- `src/RemoteAgent.App`: .NET MAUI Android client UI and platform services.
527
- `src/RemoteAgent.Service`: ASP.NET Core gRPC service that runs and streams agent sessions.
@@ -43,3 +65,4 @@ Use default/minimal verbosity. Do not pass `-q` to `dotnet build` or `dotnet res
4365
- Use concise imperative commit messages, optionally with issue references (example: `Fix Android CI job (#8)`).
4466
- Sign commits (verified signature required by branch protection).
4567
- PRs should include: purpose, summary of changes, test evidence (`dotnet test`), and docs updates when behavior/config changes.
68+

CONTRIBUTING.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,34 @@ All warnings are treated as errors. See [REPOSITORY_RULES.md](docs/REPOSITORY_RU
7676
- Ensure all tests pass before submitting a PR.
7777
- Integration tests should go in `RemoteAgent.Service.IntegrationTests`.
7878

79+
### Requirements Traceability
80+
81+
All test classes and methods should be annotated with the functional (FR) and technical (TR) requirements they cover:
82+
83+
```csharp
84+
/// <summary>Tests for authentication. FR-13.5; TR-18.1, TR-18.2.</summary>
85+
[Trait("Category", "Requirements")]
86+
[Trait("Requirement", "FR-13.5")]
87+
[Trait("Requirement", "TR-18.1")]
88+
[Trait("Requirement", "TR-18.2")]
89+
public class AuthUserServiceTests
90+
{
91+
[Fact]
92+
public void UpsertListDelete_ShouldPersistUser()
93+
{
94+
// Test implementation
95+
}
96+
}
97+
```
98+
99+
After adding or updating requirement annotations, regenerate the traceability matrix:
100+
101+
```bash
102+
./scripts/generate-requirements-matrix.sh
103+
```
104+
105+
This updates `docs/requirements-test-coverage.md`, which maps all requirements to their test coverage.
106+
79107
## Building and Testing
80108

81109
### Prerequisites

0 commit comments

Comments
 (0)