Desktop app

The desktop app is the human's control room on top of opsd: the inbox, the board, task details with every action, agents and their runs, run logs, votes and channels.

Install and updates

On Windows, AgentOps-Setup.exe from GitHub Releases installs the app for your user (under %LOCALAPPDATA%\Programs\Agent Ops unless you pick another folder). The app carries opsd and the ops command as bundles it runs with its own executable as Node, so no separate Node.js is needed:

The app checks GitHub Releases a minute after it starts and every 4 hours, and downloads a new version in the background. It installs on its own once no agent run is live and no window is open (the app sits in the tray): it stops opsd, which runs from the files the installer replaces, installs silently and starts again in the tray. Settings > Updates, the banner and the tray's Restart to update install at once; with runs live they ask first: the runs keep going and the new opsd adopts them, but their ops commands fail while the files are replaced. A development build run from a checkout never updates itself.

Run from a checkout

From the engine checkout:

npm install
npm run desktop   # builds the core, opsd and the UI, then opens the app

On the first start, when the user config has no me, the app asks for your id (default human:<OS user name>) and the data roots to load (default: the root it was started in), then writes the user config (%APPDATA%\agent-ops\config.yml, ~/.config/agent-ops/config.yml or OPS_CONFIG) and starts opsd. An existing config keeps everything, comments and roots included, and only gains a missing me. If an opsd that runs no agents is already up, the app restarts it to load the config; one that runs agents keeps going, and the app says to restart it. From a terminal, ops init --user [--me human:NAME] [--root DIR]... (or ops setup) does the same.

The app starts opsd when none runs and connects to the one that does. An opsd started before the engine was rebuilt runs an older build (its GET /api/health build differs from the one on disk): the app restarts it at once when no run is live, and otherwise shows a banner and tries again every 30 s, so no agent is cut off. Closing the window leaves opsd and every agent run going; stop them with ops daemon stop (add --kill-runs to stop the runs too; an opsd whose runs cannot outlive it refuses a stop without it while runs are live).

Background service and tray

Settings > Background service (opsd) shows whether opsd runs, is paused or is stopped, with its live runs, and has these controls:

While the app runs it has a tray icon (the notification area, bottom right of the taskbar). Its colour is the state: green running, amber paused, grey stopped, with a blue dot while something waits for you. Left-click opens the window; right-click offers the same controls as the card, the items that need you, the notification switch, and Quit Agent Ops. Closing the window keeps the tray icon; Quit closes the app, and opsd keeps running. One app runs per person: starting it again shows the open one.

Views

View What you do there
Inbox What waits for you, grouped: questions to answer, reviews, decisions, tasks assigned or delegated to you, mentions; Chat with the PM opens Chat
Board Tasks by status; drag a card to another column to change its status (Working, Blocked and Review ask for what they need); swimlanes by agent, project or priority; ideas stay hidden until you turn on Ideas
Tasks Search and filter by status, project, owner, role, provider, priority, type and label; sort by priority or update; due dates
Findings Bugs, risks and ideas agents filed with ops finding: accept as a task or bug with a priority and owner, set aside as an idea, or park
Standups Per agent and person: done, in review, working on, blocked, runs and tokens, from the task history; #standup posts
Activity Every task event and channel post across roots, newest first by day; filter by who, project and kind (work, reviews, talk, runs, findings and notes)
Reports A week, 30 or 90 days: tasks opened, closed, parked, open and blocked per project; runs, minutes and tokens per provider and agent; decisions taken; channel posts
Task panel Status and priority menus, owner as a tag, the conversation oldest first with each answer linked to its question, comments with @ mentions and Notify, delegate with a due date and time, run an agent
Agents What starts runs (wake, Run agent, schedules), Create role, each provider's usage and pause, work waiting with why, machines, agents by provider with live runs and backlog, agents ready without a role file
Runs Every run with its log, followed live; readable or raw; its processes and Kill leftovers; Start a run
Schedules Recurring runs (opsd's cron): add, edit, pause, Run now, delete; templates for standups, inbox sweeps, nightly QA
Repeating Repeating tasks: a template that creates a task once, daily, weekly, on chosen weekdays, monthly, yearly, or when a task or all its subtasks close; add, edit, pause, Run now, delete; state, next slot, last task
Chat One conversation with the orchestrator: replies stream in, what it changed shows as task links, @*:orchestrator posts from channels join it; Stop, New conversation (see the supervisor)
Channels New channel, read and post; @id in a message puts it in that inbox
Decisions Tallies and ballots; decide ties and votes without quorum
Knowledge Learn notes from kb/ by topic; search with the same matching as ops kb; links to the task a note came from
Settings opsd status and restart, health checks (same as ops doctor), data roots, the Master Repo and which project roots mirror into it, webhook notifications, run limits, providers, roles, which agents wake on work

Agents and people are color-coded everywhere: the tile color and mark show the provider (Claude, Codex, a person), the corner badge and the role name's color show the role (engineer, QA, PM, docs). Priorities show as Urgent, Medium, Standard and Low for p0 to p3; agents and the files keep p0 to p3. A due date alone means the end of that day; add a time in the picker for an exact one.

The status menu and the board offer only moves the protocol has one action for: To do releases, unblocks, reopens or requests changes; Working starts work (you take it, or an agent run starts); Blocked asks why and on whom or what; Review asks for the reviewer; Done closes; Parked asks why. Other moves are dimmed with the reason. Every move, from the status menu, a board drop or the palette, opens a dialog and changes nothing until you click its button there, so no stray pick, drop or Enter closes or reopens a task in your name.

Text that agents and people write shows as Markdown wherever it appears: the task description, events and checkpoint in the task panel, Inbox messages, questions, review requests and triage questions, Activity, Decisions (question, context, ballot reasons, outcome), Chat, Channels, Standups, Knowledge, the Agents view's waiting list, schedule prompts and the task description of each repeating task. Headings, lists, code and links render; task ids are links with a status dot and @id mentions are colored pills. Titles stay one line. In lists (Inbox, Activity, Agents, Schedules, Repeating) long text shows its first lines; click it or Show more to expand it, and again to fold it. See event text for how agents write it.

Chat with the PM, at the top of the sidebar and in the Inbox, opens Chat: one conversation with the project's PM (the orchestrator), with no agent to pick. An answer never closes it, and it survives a restart of the app. A task of type question that an agent answered keeps its reply box: Reply reopens it with your text as the next round, and its owner answers again.

Settings writes the user config with its comments kept, and opsd applies the change at once. Through opsd's API it changes limits, roles, a provider's model, max_live, pause_at and paused, removes a provider or resets a seeded one to default, adds, edits and removes agents (wake, budget, model), sync and roots only.

Answer and Comment. While a task waits on you, Answer closes the question and puts the task back to to do, so the agent goes on; Comment adds a note and the task keeps waiting. Ctrl+Enter sends the highlighted button: Answer while the task waits on you, else Comment.

The window updates when opsd reports a change (Server-Sent Events): at once after a write through the app, and within 3 seconds after an agent writes from the CLI. A message you send shows in the open task at once; an item you handle in the inbox leaves with a confirmation that links to the task. The app shows a desktop notification for each new question, review, decision or mention addressed to you.

Saved views. Board and Tasks share one filter bar. Save view names the current filters (and the board's lanes) as a view under Saved views in the sidebar. Views are stored in the app on this machine, never in a data root.

Search. The box under New task (Ctrl+F from anywhere) searches every root: tasks by id (AO-0047 or 47), title, description and every comment, answer and verdict in their threads, plus learn notes, channel posts and votes. Every word must match; "quoted words" match together. Filters narrow it: agent: (an owner, author or anyone who acted on the item; a full id, a runtime such as codex, a role such as qa, or a name such as dawid), status:, project:, type:, priority:, root:, is:open or is:closed, and in:tasks, in:notes, in:posts or in:votes. Results drop down as you type; arrow keys and Enter open one, and Enter on the query opens Search with every result, grouped by kind. The same search is GET /api/search?q=..., which answers { hits, partial }. Each request has a fixed work budget (file system calls and bytes read), so a search never blocks opsd on a large store; when a request reaches it, partial is true and the app says it searched part of the store. Each kind in each root gets a share of the budget, and root: skips the other roots before any work. Items read once stay cached, and each request picks up each folder where the one before it stopped, so searching again covers more; an item too large for a whole share is skipped.

Voice input. Every composer (New task, comments, answers, channel posts, standups) has a microphone button at its top right. Click it to start and click again to stop, or hold it and release to stop; the text goes in at the cursor, or replaces the selection. Recordings stop after five minutes. Turn it on in Settings > Voice input; it is off by default and then records nothing.

The settings live under desktop.voice in the user config: engine (off, local or cloud), program, model, language (auto or a code such as pl) and cloud_model.

Keyboard. Ctrl+K (Cmd+K on macOS) opens the command palette: find a task by id or title, go to a view, create a task, or move the open task (start, review, block, done, back to to do) through the same calls as the board. Outside text fields, n starts a new task, / focuses the view's search, g then a letter goes to a view (g b board, g l tasks, g i inbox, g c channels, g k knowledge), and ? lists every shortcut.

Back and Forward in the task panel. Following a task link inside the open task panel (a thread, a comment, the parent, a blocked-on task, a message) keeps the task you came from. Back in the panel header returns to it, and Forward goes again after Back. Alt+Left, Alt+Right and the mouse back and forward buttons do the same outside text fields. A link to the task already open adds no step. Closing the panel clears the history, so a task opened from a view with no panel open has no Back.

Security

Screenshots for checks

AGENT_OPS_SCREENSHOTS=<dir> renders the app offscreen, saves one PNG per view in AGENT_OPS_VIEWS (default every view; task:<id> and runs:<id> open one task or run), and quits. It needs no GPU or visible desktop.

Smoke check

npm run smoke:desktop (after npm run build:desktop) renders every view offscreen against a scratch data root and fails when a view logs an error, an opsd call fails, or the allowlist refuses a call. It stops the scratch opsd it started. The release workflow runs it on Linux under xvfb-run for every v* tag.