Skip to main content

AgentUp.Desktop

AgentUp.Desktop is the human UI for Agent-Up.

Technology:

  • .NET 10
  • Avalonia 12 (Fluent dark theme)
  • ReactiveUI (MVVM)

Structure

The project follows capability-oriented slices:

AgentUp.Desktop/
Features/
Workspaces/
DTOs/ WorkspaceDto
Providers/ WorkspaceApiClient
Repositories/ BrowserUrlStore
ViewModels/ MainViewModel, WorkspaceItemViewModel, WorkspaceCloneViewModel
Views/ MainWindow.axaml
Git/
DTOs/ GitChangeTreeDto, GitFileDiffDto
Providers/ GitApiClient
Services/ GitChangeListService
Controllers/ GitController
ViewModels/ GitPanelViewModel, GitChangeNodeViewModel

Responsibilities

The Desktop displays:

  • Workspaces.
  • Browser tabs.
  • Logs.
  • Diagnostics.
  • Health.
  • Running processes.
  • A paginated frontend audit trail for each selected application.
  • The uncommitted Git changes of the selected workspace.

It connects to the Server and renders server-owned state.

Adding Workspaces

The workspace list header has a + button that opens a modal asking for a repository and a branch. Confirming posts to the Server's source-clones endpoint, reloads the list, and selects the workspace the Server registered. The dialog stays open with the Server's message when the clone fails, so the user can correct the repository or branch without retyping both.

Desktop does not clone, validate remotes, or choose a destination directory. Those are Server concerns; Desktop only collects the two fields and renders the result.

Git Panel

The Commit tab of the selected workspace is the Git review surface. It renders the Server's change tree as a flattened, indented list with a Changes checkbox at the root that selects every file, then directories, then files, each row carrying a checkbox and a status marker. Discard asks for confirmation, then restores or deletes the selected files. Branch switching is on the Overview tab, not in this panel.

Selecting a file name opens its diff in a modal over the window. Selecting a directory checkbox selects every file beneath it, and a directory shows as checked exactly when all of its files are selected. Below the list are a commit message box and a Commit button that stays disabled until at least one file is selected and the message is non-empty.

GitPanelViewModel owns selection propagation between directory and file rows; the flattened rows keep the Avalonia list simple while the Server keeps the tree shape. The panel reloads whenever the selected workspace changes, after a successful commit or discard, and on a short poll while it is open. Checkboxes for files that are still present are kept across those reloads. The same view-model also drives the workspace branch dropdown and create-branch field.

First-Run Tutorial

On first start, the Desktop shows a required setup tutorial over the normal application shell unless the user has already completed or skipped it. Tutorial progress is stored in the Desktop user settings file under the user's local application data directory.

The tutorial is a Desktop concern because it guides local workstation setup before the user can use Agent-Up successfully. It must not duplicate Server orchestration state. It may run local readiness checks that are prerequisites for using the Desktop, such as verifying that the Docker CLI is installed, that the Docker engine is responding, and that Agent-Up CLI can be invoked.

The first-run flow is:

  1. Check Docker and Agent-Up CLI. Docker CLI availability is checked with docker --version; engine readiness is checked with docker info. Agent-Up CLI is checked with agent-up --help; if that is unavailable, Desktop may fall back to dotnet run --project <repo>/AgentUp.CLI/AgentUp.CLI.csproj -- --help when the CLI project can be inferred from the Desktop binary location. The user cannot continue until Docker, the Docker engine, and one Agent-Up CLI path work.
  2. Choose a development environment from a visual grid. The JavaScript path is the active path and creates a React SPA dashboard, Express API, and Postgres sample.
  3. Check Node.js by running node --version and npm --version. The JavaScript path requires Node.js 20 or newer. If Node is installed while Desktop is already running, the tutorial tells the user to restart Desktop so the process receives the updated PATH.
  4. Create the sample project files. Desktop chooses a fresh path shaped like /tmp/<random-guid>/agent-up-tutorial/example-agent1 and writes the React dashboard, Express product API, Postgres seed data, and docker-compose.yaml. The Express API serves a built-in OpenAPI explorer at / and the raw OpenAPI document at /openapi.json. If the step is repeated and the previous path already contains project files, Desktop chooses a new GUID root. The smoke check for this step is that the expected project files, including docker-compose.yaml, exist. Desktop also provides an Open File Explorer button for the generated folder.
  5. Create agent-up.json at the project root. The tutorial shows the copyable file and also provides an auto-create button. The check for this step is that agent-up.json exists and is parseable with at least one application.
  6. Run agent-up start in the sample project directory. The tutorial provides an automatic button that uses agent-up start or the local CLI project fallback. The check for this step is that the Server registered a workspace containing React SPA, Express API, and Postgres.
  7. Duplicate the sample directory. Desktop copies example-agent1 to example-agent2 under the same /tmp/<random-guid>/agent-up-tutorial/ root, swaps in different product seed data, and runs agent-up start in the duplicate. The tutorial checks that two matching workspaces exist and that their allocated ports do not collide.
  8. Show a success page explaining that the user has just created two isolated workspaces from the same JavaScript project, that the second workspace intentionally shows different product data in the dashboard, and why Server-owned workspace identity, processes, Docker, browser profiles, diagnostics, and ports matter.

Completing the final step saves the tutorial as completed. The Skip button hides the entire tutorial and persists skipped state, so it does not reappear on the next launch.

Multi-action steps reveal one section at a time. Follow-up actions such as Open File Explorer, smoke checks, agent-up.json checks, workspace checks, and duplicate checks should only appear after the preceding action has been taken. The Continue button remains disabled until the step's final check succeeds and the result banner is visible. The project-file step renders a directory tree from .NET filesystem APIs instead of relying on a platform tree command. The agent-up start step shows command output before revealing the Server workspace check.

Users can navigate back to a previous step. Going back invalidates that step and all later step checks, so the user must complete them again.

The tutorial is rendered as an overlay above the full Desktop window. The underlying application remains visible behind a dimmed blurred layer, but the overlay consumes input while it is visible.

Console output should render as one wrapped, multiline-selectable text surface for copying diagnostics, but not editable.

Generated tutorial sample dependencies should be pinned to known-compatible versions instead of latest, so the first-run flow does not break because of upstream package engine changes. The generated agent-up.json commands clear stale node_modules and package-lock.json before installing, so rerunning the tutorial does not keep an incompatible Vite or native bundler package from an older sample. The generated Postgres command starts the Compose service and then streams docker compose logs -f database, so the Desktop console shows database readiness instead of only detached Compose status lines.

Native WebView surfaces must be hidden while any in-window modal overlay is visible, including the first-run tutorial, add-workspace dialog, workspace delete confirmation, and Git file diff. Native browser widgets can render outside normal XAML z-order, so the Desktop explicitly hides active port and console WebViews and browser error banners during those overlays and restores the active WebView after the overlay closes.

While the tutorial overlay is visible, Desktop reloads the workspace list and asks the active browser view to reload after every step transition. This keeps the application state behind the overlay current without letting Desktop own workspace orchestration.

Native Desktop E2E tests set AGENTUP_SKIP_FIRST_RUN_TUTORIAL=1 so onboarding does not cover the application surface under test.

Browser Experience

The desktop should visually align with the interactive demo on the docs marketing page: compact dark chrome with no outer frame border, subtle internal dividers where needed, green/teal active states and indicators, rounded workspace entries, and a browser-first runtime surface.

The app owns its window chrome. Do not rely on the host Xorg/desktop title bar for primary controls. Workspace reload, Server connection badge, title, and window controls are built into the top navigation area so screenshots and the real desktop app use the same frame. Window controls sit on the top right in Windows order: minimize, restore, close. The Server badge sits on the left after the reload control and is green when the Desktop can reach the Server and red when it cannot.

Desktop sets a runtime WindowIcon from media/logo.png so Linux/Xorg window switchers can display the app icon. The Desktop project must also declare ApplicationIcon pointing at media/logo.ico; Windows shell surfaces such as Alt+Tab use the executable icon resource rather than only Avalonia's runtime window icon.

+---------------------------------------------------------------+
| ↺ SERVER ONLINE Agent-Up _ □ × |
+---------------------------------------------------------------+
| Workspaces [+][‹] | Overview Agent Commit | apps | Validation [›] |
| Agent1 |-----------------------------------------------|
| Agent2 | Overview, agent, commit, or app |
| | |
+---------------------------------------------------------------+

The left side shows workspace selection, health, branch, and running state. Running and starting workspaces always appear above stopping, stopped, or failed ones; within each group the Server orders by recent activity. Desktop preserves that order on reload and re-sorts live when workspace state changes. Workspace and validation sidebar collapse controls sit in each sidebar header, not in the title bar. Expanded workspace rows fill the sidebar width, use the last segment of the repository path as the title, show the branch underneath, and expose the full repository path as the hover tooltip. Selecting a workspace opens its Overview tab. The Overview tab shows workspace identity, the live branch picker, and Server-owned CPU, memory, storage, and process totals that stretch with the content pane. Switching workspaces shows a loading scaffold until the new overview arrives instead of keeping the previous workspace's metrics. Agent chat and Git commit review have their own tabs before a separator; application tabs follow that separator. Validation checks stay in a right sidebar that follows the currently selected application. Application port controls, Console, Metrics, and Diagnostics appear only after an application tab is selected. The center contains the embedded browser when an HTTP application port is selected.

Workspace Tabs

The first tab row has two groups. Overview, Agent, and Commit are workspace surfaces. A small | separator follows them, then the applications configured for the selected workspace. Selecting an application rebuilds the second tab row for that application. Switching to Overview, Agent, or Commit keeps the internally selected application so returning to an app tab restores its port, Console, or Metrics surface. The Validation sidebar stays open beside those surfaces and reloads for that selected application.

For applications with configured ports, the second row starts with ports in agent-up.json order and automatically selects the first configured port. This makes the app's primary browser surface the default when switching between applications. Console and Metrics remain available after the port tabs.

The Metrics tab shows a vertically scrollable dashboard of summary cards and time-series charts built from Server-pulled application metrics (ports[].metrics in agent-up.json). Data refreshes every 30 seconds while the tab is selected.

For applications without configured ports, Console is selected by default.

When the selected tab is an HTTP port, the Desktop shows a third row with back, forward, and reload controls followed by an editable browser address field. The field contains the full URL, such as http://localhost:3000/, for the selected port. Pressing Enter navigates the embedded WebView directly to that URL, and successful HTTP/HTTPS browser navigations update the field to the current page URL. Non-HTTP ports do not show the address row.

Switching from an HTTP port tab to Console, TCP info, or another non-browser surface must hide and restore the existing embedded WebView without reloading or re-navigating when the WebView is already at the requested URL. Switching to a different application URL may navigate the WebView, but duplicate same-URL tab-return navigation is forbidden because it resets scroll position, JavaScript memory, and open UI state.

Same-URL navigation is still required for reconnect paths such as application state changes after a restart. Desktop tab-selection navigation must request non-reloading activation of an existing WebView page, while app-state wake, address-bar, reload, and explicit navigation paths must keep reload-on-same-URL behavior.

Because the native WebView does not always raise managed navigation updates for in-page link clicks, the Desktop polls the active HTTP WebView source URL and mirrors HTTP/HTTPS changes into the address field. The poll timer runs only while an HTTP port tab is visible.

Health-check SSE events that include port health are applied in-place; the Desktop skips the follow-up workspace refresh for those events to avoid redundant HTTP traffic and UI cascades.

Reloading workspaces keeps the selected workspace by ID but rebinds it to the refreshed Server state, so the selected application's active HTTP port is navigated again after a sidebar reload.

Workspace event invalidations are scoped to the event workspace ID. Desktop refreshes only that workspace from the Server and must not rebuild or navigate unrelated workspace browser sessions when another workspace changes ports or state.

Browser Sessions

Desktop displays each workspace application through a direct embedded WebView connection to the allocated HTTP port. Desktop owns those WebView instances and their browser state on the workstation.

Desktop bridges HTML file inputs to the native Avalonia file picker so uploads work consistently across the platform WebView engines. Only trusted user clicks reach the bridge, so a page cannot open a native file chooser on its own. The selected files are returned only to the requesting WebView and are limited to 32 MB per file and 128 MB per selection, enforced while each file is read rather than after it, so an oversized selection is refused instead of buffered. Directory inputs continue to use the platform WebView behavior.

The upload bridge and the sign-in popup path are covered end to end in AgentUp.Tests against the real window, the platform WebView engine, and the platform storage provider. Those tests substitute only what no test runner can drive on a CI runner — the modal file chooser and the engine's new-window callback — and keep the injected scripts, WebView messages, IStorageFile reads, redirects, and cookies real.

The Server owns a separate headless Chromium profile per workspace under browser-profiles/{workspaceId}. MCP browser tools use that headless profile for automation. Desktop does not stream, mirror, or read from the headless session.

Desktop WebViews and Server headless profiles do not share cookies, local storage, session storage, IndexedDB, cache, or navigation state. Treat them as two independent browsers for the same workspace.

Within Desktop, each HTTP port tab keeps its own WebView state when switching tabs or applications. Within the Server headless profile, MCP browser actions share one automation session per workspace. Across workspaces, both surfaces remain isolated.

Browser automation and inspection for agents should expose navigation, reload, semantic interaction, HTML/DOM capture, accessibility data, screenshots, history, and page metadata through Server-owned MCP contracts. Prefer structured inspection and accessibility data over raw HTML when generating diagnostics or automation.

Thin Client Rule

The Desktop does not own runtime state and should not duplicate orchestration rules from the Server.

Installed Runtime

Installed Desktop artifacts are paired with a local AgentUp.Server service. The installer or package service assets are responsible for installing and starting agent-up-server; the Desktop still behaves as a client and connects to http://localhost:5000 by default.

For development, AGENTUP_SERVER_URL can point Desktop at a manually started Server.

Desktop uses a persistent integrated window chrome row for the full application lifetime. The centered Agent-Up logo and right-side window controls always stay visible. The content area below the chrome switches between pages such as sign-in and the workspace shell.

Each page registers its own left-side chrome items through WindowChromeViewModel.LeftItems. The workspace page contributes the workspace refresh action and server status badge. The sign-in page registers no chrome items.

Desktop queries /api/auth/status before loading workspace state. When the Server requires authentication it shows an in-window administrator sign-in page that gates the main UI and uses the returned bearer token for REST and workspace-event requests. When the Server has authentication disabled, Desktop opens the main window directly.