Problem or use case
"What version of Omnigent am I on, and is it old?" has no answer in the web UI's app-level chrome.
The data exists and is already on the client. /api/version returns {"version": ...} (omnigent/server/app.py ~L1688) and the boot capabilities probe carries server_version into lib/capabilities.ts (~L113). But the only place it renders is a footer line inside the per-session agent-info dialog — components/AgentInfo.tsx ~L1301, server ${serverVersion} · host ${hostVersion}. To read your server version you must (a) have a session open, (b) have one whose bound agent passes agentHasInfo, (c) open the header three-dot menu, (d) open agent info, (e) scroll to the footer. On the landing page, Settings, Inbox, or Automations there is no path to it at all.
Settings → Updates doesn't cover this either: it is the Electron auto-updater's preferences for the installed desktop shell (pages/SettingsPage.tsx ~L1553, "Desktop app update preferences for this installed Omnigent shell"). It says nothing about the server you are connected to, and it doesn't exist in a browser.
The staleness half is worse — nothing in the web UI ever says a newer release exists. That check does exist, but only in the terminal: omnigent/update_check.py runs maybe_show_update_notice() on CLI invocation, resolves the configured index (uv.toml / pip.conf, default pypi.org), fetches the latest release via fetch_latest_version(), compares with _is_newer(), and prints a one-time notice per new release. It also already reads install_time_epoch from the installed wheel's dist-info, so "how old is this install" is a resolved quantity today. A web-only user — the desktop app, a phone, a remote server behind a tunnel — never sees any of it.
Practical consequences:
- Filing a bug means going and finding the version, or guessing.
- Servers drift. Long-lived remote/tunneled deployments are the ones most likely to be months behind and least likely to have someone watching a terminal.
- A user hitting a bug fixed three releases ago has no in-app signal that upgrading is the fix.
Proposed solution
Affordance. An info icon next to the Omnigent wordmark in the sidebar header (shell/Sidebar.tsx ~L707, data-testid="sidebar-wordmark"), in the existing sidebar-header-actions cluster styling — Button variant="ghost" size="icon-xs" with a TooltipContent side="bottom", matching the Search and adjacent buttons already there. Do not attach it to the wordmark's own click target: the <Link to="/"> is the app's only way back to the landing composer (its inline comment says so). A sibling icon button, not a hijacked brand mark.
Clicking opens a small popover:
- Server —
server_version from the capabilities probe, with a staleness line beneath it.
- Host — for a host-bound session,
host_version from useRunnerHealth (host_version is already plumbed there), so the existing agent-info footer's pair is reachable app-wide.
- Client — the web build version/commit, if it's already available; skip rather than invent a new build stamp.
- A link to the release notes / GitHub releases.
Staleness. The honest version, in order of increasing cost:
- Age of this build.
_read_build_info() / install_time_epoch in update_check.py already yields the install timestamp. Expose it alongside the version in /api/version (and the capabilities payload) and render "installed 34 days ago". No network call, no new dependency.
- Behind by N releases. Reuse
fetch_latest_version() + _is_newer() server-side on the existing cadence and cache (_read_cache / _is_stale), and return latest_version + update_available in the same payload. The UI then reads "0.4.1 — a newer release (0.5.0) is available", with a neutral/amber dot rather than a nag banner.
Doing (1) first is worthwhile on its own; (2) is the one that answers "is that old?" properly, and it is mostly wiring an existing module to an existing endpoint.
Privacy / offline. The PyPI lookup must stay opt-outable and must never block the popover — the module's index resolution already respects corporate mirrors via uv.toml / pip.conf, and an air-gapped server should degrade to showing the version and install age with no error state. A failed check renders nothing extra, not a warning.
Acceptance criteria
- An info button sits beside the sidebar wordmark on every page, with no session required, and does not disturb the wordmark's "home" link behavior.
- Its popover shows the server version, and the bound host version when there is one.
- The popover shows how old the running server build is (install age at minimum).
- When a newer release is detectable, the popover says so and names the version; when it isn't detectable — offline, air-gapped, opt-out, mirror without the metadata — the popover renders cleanly with no error.
- The version check never blocks first paint of the popover.
- The existing agent-info footer keeps working (this doesn't move it).
- Tests: Vitest for the popover's render states (version present / absent, update available / not / unknown); pytest for whatever
/api/version gains.
Alternatives considered
- Put it in Settings only. Better than today, but Settings → Updates is Electron-specific and browser users have no equivalent page; the sidebar header is visible everywhere and costs one icon.
- Reuse the wordmark itself as the trigger (click → info). Breaks the only route back to the landing composer, which
Sidebar.tsx calls out explicitly.
- A global "update available" banner or toast. Higher-pressure than warranted for a self-hosted server the user may not control, and impossible to dismiss well. A dot on an info icon is the right volume.
- Version only, no staleness. Answers half the question. "0.4.1" means nothing to a user who doesn't track the release cadence — the age is the part that's actionable.
- Client-side call to PyPI/GitHub from the browser. Leaks the deployment to a third party from every user's browser, breaks under CSP and offline installs, and duplicates a check that already exists server-side with a cache.
Harness
Not applicable
Platform or device
Not platform-specific
Harness mode
Not applicable
Expected reach
Most users
Authentication type
Not authentication-related
Problem or use case
"What version of Omnigent am I on, and is it old?" has no answer in the web UI's app-level chrome.
The data exists and is already on the client.
/api/versionreturns{"version": ...}(omnigent/server/app.py~L1688) and the boot capabilities probe carriesserver_versionintolib/capabilities.ts(~L113). But the only place it renders is a footer line inside the per-session agent-info dialog —components/AgentInfo.tsx~L1301,server ${serverVersion} · host ${hostVersion}. To read your server version you must (a) have a session open, (b) have one whose bound agent passesagentHasInfo, (c) open the header three-dot menu, (d) open agent info, (e) scroll to the footer. On the landing page, Settings, Inbox, or Automations there is no path to it at all.Settings → Updates doesn't cover this either: it is the Electron auto-updater's preferences for the installed desktop shell (
pages/SettingsPage.tsx~L1553, "Desktop app update preferences for this installed Omnigent shell"). It says nothing about the server you are connected to, and it doesn't exist in a browser.The staleness half is worse — nothing in the web UI ever says a newer release exists. That check does exist, but only in the terminal:
omnigent/update_check.pyrunsmaybe_show_update_notice()on CLI invocation, resolves the configured index (uv.toml/pip.conf, defaultpypi.org), fetches the latest release viafetch_latest_version(), compares with_is_newer(), and prints a one-time notice per new release. It also already readsinstall_time_epochfrom the installed wheel's dist-info, so "how old is this install" is a resolved quantity today. A web-only user — the desktop app, a phone, a remote server behind a tunnel — never sees any of it.Practical consequences:
Proposed solution
Affordance. An info icon next to the Omnigent wordmark in the sidebar header (
shell/Sidebar.tsx~L707,data-testid="sidebar-wordmark"), in the existingsidebar-header-actionscluster styling —Button variant="ghost" size="icon-xs"with aTooltipContent side="bottom", matching the Search and adjacent buttons already there. Do not attach it to the wordmark's own click target: the<Link to="/">is the app's only way back to the landing composer (its inline comment says so). A sibling icon button, not a hijacked brand mark.Clicking opens a small popover:
server_versionfrom the capabilities probe, with a staleness line beneath it.host_versionfromuseRunnerHealth(host_versionis already plumbed there), so the existing agent-info footer's pair is reachable app-wide.Staleness. The honest version, in order of increasing cost:
_read_build_info()/install_time_epochinupdate_check.pyalready yields the install timestamp. Expose it alongside the version in/api/version(and the capabilities payload) and render "installed 34 days ago". No network call, no new dependency.fetch_latest_version()+_is_newer()server-side on the existing cadence and cache (_read_cache/_is_stale), and returnlatest_version+update_availablein the same payload. The UI then reads "0.4.1 — a newer release (0.5.0) is available", with a neutral/amber dot rather than a nag banner.Doing (1) first is worthwhile on its own; (2) is the one that answers "is that old?" properly, and it is mostly wiring an existing module to an existing endpoint.
Privacy / offline. The PyPI lookup must stay opt-outable and must never block the popover — the module's index resolution already respects corporate mirrors via
uv.toml/pip.conf, and an air-gapped server should degrade to showing the version and install age with no error state. A failed check renders nothing extra, not a warning.Acceptance criteria
/api/versiongains.Alternatives considered
Sidebar.tsxcalls out explicitly.Harness
Not applicable
Platform or device
Not platform-specific
Harness mode
Not applicable
Expected reach
Most users
Authentication type
Not authentication-related