Skip to content

v0.7.0

Choose a tag to compare

@github-actions github-actions released this 14 Nov 04:08
· 9 commits to main since this release

Breaking changes

  • ompweb ships its own omp, ompweb-omp; omp no longer needs installing. Every session runs on ompweb's own omp build: omp's CLI from an ompweb-dev/oh-my-pi integration release (an upstream omp release plus the PRs it carries), with ompweb's channel instead of omp's RPC mode. It reads the same ~/.omp settings, logins and sessions. Each release attaches ompweb-omp-<platform> for linux-x64, linux-arm64, darwin-x64, darwin-arm64 and windows-x64, with their SHA-256 sums; every install, npm and npx included, downloads the one of its exact version on its first start, checks it and keeps it (beside the single binary, otherwise in <agent dir>/omp-web/omp/<version>/), and ompweb fetch-omp does so on demand. Sessions start once it is there. On any other platform ompweb cannot start sessions. The release archives do not contain it; prerelease archives do. omp updates with ompweb: both updaters fetch the new version's runner before its preflight. OMP_WEB_OMP_BIN now points at another ompweb-omp build, for example a copy of the release asset on a machine that cannot reach GitHub: a stock omp does not speak the channel and cannot run ompweb's sessions. On Linux and macOS, ompweb-omp run in a terminal works like omp's TUI, and its session also shows up live in the web.
  • Link protocol 11 only. Web servers and host daemons on this release speak link protocol 11 and refuse every older peer, prereleases on protocol 10 included: update the web server first, then every host. Until a host is updated, Settings → Hosts says it needs an update and no session starts there.
  • Every host needs one manual update. A host daemon on an older release speaks link protocol 10 or below, so a web server on this release refuses its link, and Settings → Hosts cannot update it (an older web server cannot either: its hosts refuse a candidate on a newer protocol). Update each host by hand once, after the web server: for npm, restart its ompweb host or service (npx @kahme247/ompweb@latest picks up the new version; a global install needs npm install -g @kahme247/ompweb first); for a single binary, stop ompweb host, replace its directory with the new release and start it again. Either downloads its ompweb-omp on that start. Later updates from Settings → Hosts work, and fetch the new version's runner before its preflight.
  • The service installers no longer pin a stock omp. ompweb systemd install and ompweb-launchd install used to write the omp found on PATH into the service environment as OMP_WEB_OMP_BIN, which would now make every session fail. They write OMP_WEB_OMP_BIN only when you set it yourself. Reinstall the services after updating if your service environment (~/.omp/agent/web-service.env, the unit or the plist) still names a stock omp.
  • Skill startup notices and "View skill diagnostics" are gone. They needed omp commands that only an unmerged omp PR had; the notices above the composer, the Settings → Interface & Behavior switch and the Skills panel's View skill diagnostics are removed.

Added

  • A distinct ompweb icon. The rounded π inside an open orbit retains the pink–purple–cyan palette. The sidebar logo, SVG/PNG artwork, favicons, installed-app icons and tray icon now use the same mark.

  • Upgrades check the session omp before they switch. ompweb preflight resolves omp exactly as the host does for a new session (OMP_WEB_OMP_BIN or this version's downloaded ompweb-omp), starts it headless with --no-session, and waits up to 30 s for its hello: it prints preflight ok: <omp> <version>, or exits 1 naming the binary and why (not found, not executable, exited with its code, or no hello with omp's last stderr). The host updater and the web app's in-app update run the new release's preflight in the live environment before they stop anything, and abort with the current install still running when it fails. Before the preflight, both run the new release's ompweb fetch-omp, so its runner is in place first. A release without the preflight command is checked only if it ships ompweb-omp; then the update fails.

  • Rename the cluster. Settings → Hosts shows the cluster's name with a Rename cluster button; the sidebar group follows. The cluster is still created as "Cluster" when the first host joins, so rename it afterwards. PATCH /api/hosts { poolId, label } renames a pool without replacing the pool registry.

  • Cluster members use this machine's omp configuration.
    Before upgrading: every cluster member needs an ompweb host daemon at link protocol 10. Until a member's daemon is updated, sessions cannot start there (the start is refused and names the update), cluster placement skips it, a host below protocol 10 cannot join, and Settings → Hosts says it needs an update. Once its daemon is updated, every host already in the first pool receives this machine's configuration, secrets included (API keys, MCP environments and headers, tokens), automatically and without a confirmation having been shown, and its own omp configuration is replaced until it leaves the cluster; it is kept in <agent dir>/omp-web/own-config/ on the member and put back on leaving. To avoid that for a host, remove it from the first pool (PUT /api/hosts, or editing ~/.omp/agent/omp-web/pools.json) before updating its daemon. Update the web server first, then the members.
    A cluster session now runs with this machine's settings (config.yml, without the machine-specific keys), models.yml, MCP servers, skills, task agents, rules and context files, commands, prompts, hooks, extensions, custom tools, secrets.yml, .env, the agent environment variables of Settings → System, and the installed plugins, whichever member it runs on. A change made here, in Settings or on disk, reaches the members within about a minute and before every session start on a member. Every cluster session start carries this machine's version: a start that must run on one member is refused with the reason when its files cannot be applied, and new sessions are placed only on members with the current configuration and plugins; a session recovered after a daemon restart, or started by another ompweb server, runs on whatever is on the member's disk, and running sessions are not restarted. A mirrored directory becomes equal to this machine's. The member's backup holds only what the host had when it joined: a file it adds or edits afterwards is deleted at the next apply, not backed up. Settings that describe the machine itself stay the member's own as they were when the host joined (see the README). A host holds one ompweb server's configuration: a host already in another server's cluster cannot join (409 host_in_other_cluster), and a member that also runs sessions for another server runs them on this one's configuration. The configuration contains secrets, which are written to every member's disk, readable by the daemon's user only; the hooks, extensions, tools and plugins in it run on every member, and this machine can already start omp with full tool access there. Each file is limited to 5 MiB and the configuration to 50 MiB; a configuration over a limit, or one that cannot be read, is not mirrored at all, a cluster session start on a member is refused (409 cluster_config_invalid), placement skips every member, and Settings names the file. Each download has a 30 s limit including the body (a 5 MiB file needs roughly 1.4 Mbit/s). An apply is refused on a member where ~/.agents is a symbolic link, and the mirrored directories and the backup must be on one filesystem. The reverse proxy must pass /api/hosts/cluster-config/manifest and /api/hosts/cluster-config/file/<hash> without its own authentication, at exactly these paths (unlike the broker's prefix).
    Plugins are installed (this needs bun and network access on the member), removed and enabled or disabled on each member by omp plugin: npm plugins are pinned to this machine's version, marketplace plugins have no version pin in omp, and a failing install leaves the member not current and is retried. Leaving puts the member's own configuration and plugins back; if that fails Settings says so, and it is retried while the host is linked. Revoking a member does the same: its daemon puts its own configuration back at once if it is connected, otherwise the next time it reaches this machine. Settings → Hosts shows each member's state (same as the master, updating, failed with the reason, held by another server, needs an update) and the MCP server commands that are not installed there, and names the file when this machine's configuration cannot be mirrored (the member lines then do not say updating). For a session on another member, the MCP, Skills, Plugins and Agents screens act on this machine's user-level configuration, and say they apply to every member, as does Settings → System next to the agent environment variables in a cluster session; MCP shows that session's live status read-only, and the session's MCP switches, reload and Skills diagnostics are not available there. GET /api/plugins, POST /api/plugins, GET /api/skills, POST /api/skills/check and POST /api/skills/update now accept a request without cwd for the user level, and GET /api/mcp?attach=1 reads a running session's live MCP status without starting one. A provider in models.yml that points at this machine's own address, MCP servers started by a command, and API keys written as an environment-variable name or !command need what they refer to on each member.

  • Cluster membership in Settings. There is one cluster, the first pool in pools.json, and this machine is its master. Each linked host has an In the cluster switch in Settings → Hosts; turning it on asks for the folder on that host where the cluster keeps the checkouts it creates and says what changes, turning it off asks for confirmation. A host can join while it is online, on link protocol 10, answering gitOrigins and running omp 15.13.1 or newer (a member always uses this machine's provider logins); otherwise the switch is disabled and says why. While this machine is not a member yet, any join adds it too: the switch then also needs this machine's daemon ready, and the dialog says that this machine's Git projects become cluster projects whose new sessions start on any member with capacity (currently Linux hosts only, so a macOS or Windows master runs none itself). Joining matches the host's Git projects to cluster projects by repository, so the same repository on several members is one project and its sessions are listed together (git@github.com:a/b.git and https://github.com/a/b match); adding a cluster project by another URL form of a repository it already has returns that project. A created cluster project keeps the name and alias the host showed (its folder name when it had none), and a visible checkout makes a matching hidden cluster project visible again. Projects without a single Git origin, subfolders and linked worktrees stay on their node. A host too slow to report its Git remotes within the time limit keeps the remaining projects on its node; leaving and joining again retries them. Leaving unbinds the matched projects, so they are listed under the host again, and keeps the checkouts the cluster created and the projects it gained from the host; hide any you do not want. Revoking a member removes it from the cluster the same way; while pools.json cannot be read, the revoke is refused. A pool disabled through the API takes no new sessions, so this machine's Git projects moved into it cannot start any. The rest of this machine's omp configuration is mirrored to members too (see the entry above). New: PUT/DELETE /api/hosts/[id]/cluster, and the host operation gitOrigins.

  • The sidebar groups the cluster. The cluster is one group holding its projects and the projects of its members that could not join, each marked with its node's name, in the sidebar's sort and pin order; a new session there starts on that node. Members no longer have a group of their own, and standalone hosts keep theirs.

  • Cluster members use this machine's provider logins. A member always runs its sessions with this machine's provider logins, whatever its own Use this server's provider logins setting says; Settings shows that toggle on and disabled. It is computed from membership, so leaving brings the host's own setting back. If pools.json cannot be read, session starts on a host are refused rather than started with other logins. Cluster placement skips a member whose daemon cannot take these logins (below link protocol 9, or a link that does not share) before preparing a checkout there.
    Before upgrading: after this update, every linked host already in the first pool uses this machine's provider logins (it can read their access tokens and stored API keys) without a confirmation having been shown. This includes a first pool disabled through the API, and a host added to the first pool later through PUT /api/hosts or by editing pools.json. Each linked member needs an updated ompweb host daemon (link protocol 9 at that release; cluster members now need link protocol 10, see the first entry) and omp 15.13.1 or newer; until its daemon is updated, cluster placement skips that member, and a start fails when no other member is eligible. Before updating, remove a host that should not receive the logins from the first pool using PUT /api/hosts or editing ~/.omp/agent/omp-web/pools.json; the old version has no membership switch.

  • Hosts can use the server's provider logins. Settings → Hosts has a per-host toggle, Use this server's provider logins, off by default; turning it on asks for confirmation. Sessions started on that host then use the logins stored on the ompweb server instead of the host's own, through omp auth-broker serve on the server and a loopback forwarder in the host daemon. The host token never enters an omp process's environment, and OAuth refresh tokens never leave the server machine; the host can read current access tokens and stored API keys. Starting a session on a sharing host needs a daemon on link protocol v9 and omp 15.13.1 or newer, and is refused otherwise rather than falling back to the host's own logins. Custom providers in models.yml, environment-variable API keys and omp settings are not shared. A reverse proxy must let /api/hosts/broker/ through without its own authentication, response buffering or redirects.

  • Windows hosts can be pool workers and accept local maintenance controls. ompweb host status, drain and resume now work on Windows through a named pipe with a random name and a per-start token, kept in <agent dir>/omp-web/host-control.json; requests without the current token are refused. Windows hosts also report CPU busy time and available memory, so pools can place sessions on them. Windows reports no load average, so CPU pressure there comes from busy time alone.

  • Archive and restore sessions on linked hosts. Archiving now works for sessions on any linked machine, not only this one. The Archived Sessions browser lists every host's archive, shows which host each session is on, and restores it on that host. A host that is offline or runs an older ompweb host is named in the browser instead of hiding its archive silently. Restore rejects a session ID already active on any registered host, so every registered host must be online to restore, even an archive on this machine; the browser names the offline host that blocks it. A per-session claim stops web-server processes that share one agent directory from restoring the same session together; it does not coordinate separate web servers that share a daemon. A restore on another host keeps running when the web server stops waiting for it (after 30 s, or when the link drops), so the session stays claimed until that host's daemon process confirms it stopped: restoring the same ID again, on any host, is refused until then instead of creating a second active copy. Only the daemon process that took the restore can confirm that, so if the daemon was replaced, restarted or shut down meanwhile, or its host was unregistered, the session stays blocked: there is no timeout and no automatic clearing, and recovery is manual (see AGENTS.md). A restored session's attachments and other files are put back before the session becomes visible, so a file attached from another tab at that moment can no longer fail the restore or block retrying it. Restore streams archives with fixed 256 MiB compressed and expanded limits and rejects unsupported session IDs before consuming the archive.

  • Traditional Chinese (zh-TW) interface language. Select Traditional Chinese (繁體中文) in the top-bar language menu. On compact layouts, open More controls first. Alternatively, let omp-web pick it automatically when the browser language is zh-TW, zh-HK, zh-MO, or zh-Hant. Localized Web Push notifications and a Traditional Chinese README (README.zh-TW.md) are included.

  • Several web apps can share one host daemon and the same live sessions. Link additional servers with ompweb host login --add --url <server> (token from stdin). Prompts, tool progress, results and dialogs stay synchronized, and restarting one web app leaves the other connected to the same omp process. Link protocol v7 moves session startup, idle cleanup and interrupted-run recovery into the host; older links remain supported without sharing their processes. Schedule the first daemon upgrade while its host is quiescent.

  • Sidebar marks sessions with an unsent draft. A session whose composer still holds text or attachments shows a small pencil beside its title, so a half-written message is easy to find again. The mark follows this browser tab's drafts.

  • Sidebar marks sessions waiting for your input. A session with an open question, confirmation or other blocking dialog shows a small question-bubble icon beside its title, next to the unsent-draft pencil. The mark clears as soon as the dialog is answered, cancelled or expires, in every open browser.

  • Identify untagged builds from the logo. Clicking or tapping the ompweb logo still reveals the version; builds made from an untagged Git commit also show its full hash and commit date (YYYY-MM-DD). Builds made on a tag keep the version-only display. The metadata is embedded in the browser bundle, so installed packages and binaries do not need Git.

  • Workspace file APIs run on the owning host. Directory listings, file metadata and previews, downloads, file search and referenced shell output go through that machine's daemon, including This machine. File downloads stream in bounded chunks rather than being limited by the host link's per-result size. A stopped or older daemon reports unavailable or unsupported instead of reading a path on the web server.

  • File tabs keep their owning workspace. Files with the same path on different hosts open in separate tabs. Switching chats does not redirect a tab's reads, Markdown links, images or downloads to the new host. Explorer and composer file suggestions follow the selected host, and confirming an agent's file-open request keeps that request's source host. Mention and reveal actions are disabled while the active chat belongs to another host.

  • Explorer uploads use the shared 2 GiB-per-file uploader on every host. The old 25 MiB file and 100 MiB batch caps are gone. Files stream into host staging before authorized publication into the selected workspace. Conflict choices preserve existing files until replacement is ready; cancellation removes pending staging, not files already saved. Preview limits are unchanged.

  • Live file watching follows the file's host. The owning daemon watches and reauthorizes the file, including atomic editor saves. Reconnecting creates a fresh watch and refreshes bytes missed while offline. Closing a tab releases only its watch; no file watcher on the web server substitutes for a disconnected host.

  • Git and worktrees work on linked hosts. Status, patches and GitHub references use the repository on that machine. Worktree create/select/remove keeps its host identity, grants new paths only there, and retains dirty-removal confirmation. Late results from another selected host cannot replace the current workspace state.

  • Browse and clone workspaces on linked hosts. The Add workspace picker navigates the chosen machine, validates clone destinations there, and registers completed clones on that host. Clone jobs retain bounded progress across a web restart; cancellation confirms process exit and safe cleanup, while interrupted connections report uncertain outcomes. Workspace URLs can retain their host with ?cwd=...&hostId=....

  • Run sessions on other machines from one ompweb. Link a machine (for example a MacBook) with Settings → Hosts → Add host, then run ompweb host login --url <server url> (the token is read from stdin) and ompweb host there. The remote machine keeps its own omp, agent directory and credentials; ompweb routes. The sidebar groups sessions by host, the new-session screen has a host picker, and chat, history, notifications and resume work on every host. If the link drops, the run continues on the remote machine and the chat shows an offline banner until the host reconnects. Agents can pass host to new_session (it defaults to the agent's own host), which asks for confirmation on a remote host unless the host allows it; an agent on a remote host always asks before it starts or messages a session on this machine. The skills, plugins, agents and MCP settings remain unavailable on remote hosts.

  • Restarting or redeploying ompweb no longer interrupts a run on this machine. "This machine" now runs through a local ompweb host daemon, installed as a second service (ompweb-host) by ompweb systemd install, ompweb launchd install and the Windows tray install. The daemon keeps the omp processes; a restarted web server adopts them, with no resume prompt, and the dialogs and browser tool calls omp was waiting on come back. The daemon is required: until it is installed and linked, "This machine" shows offline (with a banner showing the install command) and new sessions there cannot start. An ompweb run in a terminal or with npx installs no service: keep ompweb host running in a second terminal. Restarting the daemon still ends runs, and ompweb systemd restart leaves it alone. If the web server comes back on another port, the daemon follows it. A npm run dev server does not take over the daemon. "This machine" can be renamed in Settings → Hosts.

  • ompweb as a single binary, with no Node or npm. Each release now also carries ompweb-linux-x64, ompweb-linux-arm64, ompweb-darwin-x64, ompweb-darwin-arm64 (.tar.gz) and ompweb-windows-x64 (.zip): one ompweb program with the web files beside it. It takes the same options and environment variables as the npm ompweb, and runs the daemon too (ompweb host). It has no tray, service installers or in-app update: the README's "Single binary" section shows the service units, and Settings → System links the release page when an update is out. Build one with npm run build:binary.

  • Binary prereleases from main. Each commit on main that passes CI is published as a GitHub prerelease titled with its build time in ISO 8601 UTC (for example 2026-11-01T10:30:00Z; tag 20261101T103000Z-<short sha>) with the five single-binary archives. Prereleases are never marked latest, publish no npm package and trigger no update notice.

  • Settings → Hosts shows link protocol versions. Each host shows the link protocol version its daemon speaks, and the page shows the version this web app expects.

  • Attach any file to a message, up to 2 GiB each. The composer no longer refuses archives, PDFs or other binary files, and images are no longer capped at 5 MB per message. A file that cannot go inline is uploaded to the session's local:// directory (local/uploads/) on the machine the session runs on, and the message tells the agent its local:// URL and absolute path; in the chat, each such file is a link that opens it. Each image has an Inline / As file switch: Inline sends it with the message as before (omp resizes it for the model), As file keeps the exact bytes. An image too large to go inline switches to As file by itself. Uploads stream in chunks over the browser connection (or over HTTP in SSE mode) and on to the host, and show progress on each chip; Send waits until they finish. An upload resumes by itself from what the host already has when the browser connection drops, the web server restarts, the server loses the host's link, or the upload makes no progress for 2 minutes: the chip shows Reconnecting… meanwhile, in both the WebSocket and SSE modes. Retry appears only after 5 minutes of such an outage, or for a failure that waiting cannot fix. Update ompweb on the server first, then on its hosts: a host on an older build shows "Update ompweb on the session's host to attach files" when a file is attached. After an in-place upgrade, "This machine" takes uploads only once the ompweb host service (ompweb-host) is restarted, and the chip says so; restarting it ends the runs on this machine. Sent uploads are deleted after 30 days by default; Settings → System → Delete sent uploads after sets Never, 7, 30 or 90 days for every host, archived sessions included (a host on an older build keeps them).
    Before upgrading, back up sent uploads you need. The first retention-policy sync can also delete existing uploads older than the 30-day default. To opt out of that default, set uploadRetentionDays to null in the server's omp-web-settings.json before starting the upgraded server.

  • Keep a host running with the single binary. The README's "Keep a host running as a service" section has a systemd user unit (Linux), a logon scheduled task (Windows) and, for macOS, a restart script to run in tmux: launchd jobs cannot use the login keychain and SSH agent that omp sessions need. In tmux, macOS Local Network privacy blocks the daemon from LAN addresses, so link it to a URL that does not resolve to one (the server's public URL, or a VPN or tailnet name).

  • Session search shows where each session matched. While searching the sidebar, the arrow beside a session lists its matches one per line, best match first and newest first among equals, ten at a time with Load more…. Matches in the messages of the open conversation are highlighted, typos and partial words included (thinking, tool output and math are not searched; link addresses and image descriptions do not count), and clicking a match opens its session, scrolls to it and marks it as the current one, like the browser's find. Closing or clearing the search removes the highlights. Browsers without the CSS Custom Highlight API (before Chrome 105, Safari 17.2 or Firefox 140) still jump to the match but do not paint it.

  • Show tool calls outside Process details. A new setting keeps each tool call's row in the transcript instead of folding it; whether the row opens still follows "Keep tool calls collapsed".

  • Never fold tool activity. A new setting renders every turn flat, with no Process details rows at all. Long turns are paged, and jumping to a search match reveals its message even outside the current page.

  • Connection diagnostics. Turn on Settings → Interface & Behavior → Show connection diagnostics to get a Connection tab in the right panel, on desktop and mobile. It shows the transport actually in use (WebSocket or server-sent events, including fallback), connection status, reconnects, and latency statistics over the last 30 minutes: mean, p50, p95, p99.9 and max. Successful commands and sampled live events are grouped by transport. One-way times are clock-corrected estimates; web app ↔ omp includes processing, with get_state shown separately as a delivery proxy. p99.9 needs at least 1,000 samples.

  • Updates belong to each host. Settings → Hosts now shows the ompweb daemon and OMP runtime versions, available updates, and update actions for every machine, including This machine. Checks and installations run on that host. An incompatible or unverifiable daemon update stays visible but blocked, with a reason to update the web server first. A daemon update is refused while session processes or subagents run on that host (close them, then start it again), and the host refuses new sessions while it installs. The host update retains a backup and reports progress after reconnect. System & Updates is now System, containing only the web server's controls; it no longer updates or restarts a host's OMP sessions globally.

  • Messages wait safely until omp has them. In an existing chat, the composer clears once IndexedDB has stored the message, and a dimmed bubble names the delivery leg: Sending, Waiting for , Delivering, or Not sent with the reason. Browser-held messages survive reloads and closed tabs; server-held messages survive web-server restarts and host outages, and appear on every device viewing the chat. Chromium's Background Sync can deliver after the last tab closes; other browsers retry when an ompweb page reopens. Duplicate submissions share one message id. If a lost host reply leaves delivery uncertain, the message is marked Not sent and never retried automatically. A prompt that omp stops (Stop, or a failure) before it reaches the transcript is marked Not sent at once, so the messages behind it go on without waiting. Held messages offer Delete until dispatched. Follow-ups and steers become omp's own queue chips when accepted. New chats keep their existing first-message path; a browser unable to store the message keeps the composer draft instead.

Fixes & Improvements

  • Ask forms preserve preformatted text. Examples inside <pre> blocks keep their line breaks and indentation, including inside choice labels.
  • Linked sessions survive brief reconnects. A temporary host outage during recovery no longer closes the shared session channel. Cluster readiness no longer regresses when an older configuration reply arrives, and auth-broker readiness waits begin when its process has spawned.
  • The repository moved to ompweb-dev/ompweb; old links redirect.
  • omp updates with ompweb: the omp row no longer runs omp update. Each ompweb release ships its own omp runner, so Settings → Hosts shows the host's omp version with "Updated with ompweb" and no update button. Hosts refuse an omp update request, and /api/omp-update keeps only restart.
  • Sorting, pinning and reordering work in the cluster's sidebar group. The group listed the cluster's shared projects first whatever the sort, and Move up/down, drag and pins only worked among projects of the same machine, so a pinned or alphabetically earlier project of a member could not move above a shared one. The group is now one list: the chosen sort, pins and manual order apply across shared projects and every member's own projects.
  • Cluster members install plugins from the source this machine installed them from. A plugin installed here from Git (github:…, git+…) or a tarball URL is installed the same way on each member instead of as an unrelated npm package of the same name and version, and a member that already has that npm package replaces it. A plugin linked or installed from a local folder here is not installed on members; Settings → Hosts shows the member as failed and names the plugin to install there by hand. A source that installs a different version than this machine runs (an unpinned Git branch, for example) also shows the member as failed, with both versions. Credentials in a source never appear in the host daemon log.
  • Resuming an idle session no longer fails while omp is still starting. omp can take more than five seconds to answer its first commands after a cold start (model refresh, plugin updates, hooks). The startup handshake now allows 30 seconds, so a message sent to an idle session is no longer marked Not sent with RPC command get_state timed out after 5000ms. Status polls of a running session keep their five-second limit.
  • Subagents show the names the main session gives them. The subagent chips above the composer, the per-subagent rows of an expanded task call and the subagent dialog title now lead with the subagent's name (for example StartupTimeoutReview), followed by its agent type. Before, only the agent type appeared, so several subagents of one type were indistinguishable. The dialog lists the agent, model, reasoning effort and session file on labeled rows of their own, and wraps long values (the description, a long model name or file path) instead of cutting them at the dialog's width.
  • Back closes the right panel on phones. With the right panel open, which covers the whole screen, the Back button or gesture now only closes it. Before, Back went on to the page behind the panel (the sidebar, then the previous page), so a few taps meant to close the panel could leave omp-web.
  • Back closes the right panel on phones and narrow windows. With the right panel open, which covers the whole screen there, the Back button or gesture now only closes it. Before, Back went on to the page behind the panel (the sidebar, then the previous page), so a few taps meant to close the panel could leave omp-web.
  • omp's questions and forms are easier to read and answer. Every choice, in a question, a form, a pick list or an approval, is now a row of the same size, and a checked row is highlighted, including the options the agent pre-checked in a form. Text fields have visible borders and show the keyboard focus ring again, a field left invalid after Submit is outlined in red, a list's Done selecting row is marked as an action, and form sections and field labels are spaced apart. Rows and fields follow the touch-target setting, and native controls in the warm dark theme are dark.
  • A message sent while omp is busy no longer fails with "status query timed out". Before sending, the server reads the session's state; when omp took longer than 5 seconds to answer (for example while streaming a reply), the message was marked Not sent even though nothing had reached omp. It now stays held and is tried again after 5 seconds. A retry like this no longer takes back a message that another ompweb server has started sending in the meantime, so it cannot be sent twice.
  • Git commands stay on the folder's own repository. A host daemon started from a Git hook or with GIT_DIR set in its environment no longer runs Git status, file search, worktree, clone or pool preparation against that other repository. The Git panel of a bare repository now reports "not a Git repository" instead of failing. On Windows pool workers, a long creation ID no longer makes preparation fail on Git's path-length limit, and the reported CPU busy time no longer counts interrupt time twice.
  • Multiple-choice questions show which options are already checked. When omp asks one checkbox question at a time, each option now shows ☑ or ☐, and screen readers announce "checked" or "not checked". Before, every option looked unchecked, so tapping an option the agent had pre-checked unchecked it instead. In the composer, tapping a checkbox option now toggles it at once instead of waiting for Next, so choosing Done selecting next can no longer drop it. This needs an omp that sends checkbox state on select requests (can1357/oh-my-pi#18929); older omp versions show the options as before.
  • Reuse unchanged chat Markdown during streaming. Markdown bodies skip rendering when their props are unchanged, while relevant tool-result changes still refresh their message. Back/forward session navigation requests the selected session instead of downloading the full session list.
  • Listing files in a folder that is not a Git repository no longer leaves a git process running in it. The @-mention and file-search index asks Git first; when the folder is not a repository it now waits for both Git queries to exit before falling back to reading the folder. A leftover git process could briefly keep Windows from deleting or renaming that folder.
  • Dollar amounts no longer render as math. In text such as “$5 and $10”, the span between the two dollar signs turned into glued italic formula text. Single-dollar math now follows Pandoc's rule: the opening $ must be followed by a non-space, and the closing $ must be preceded by a non-space and not followed by a digit. $x^2$ still renders as math.
  • Dollar amounts no longer render as math. In text such as “$5 and $10” or “R$5 or US$10”, the span between the two dollar signs turned into glued italic formula text. Single-dollar math now follows Pandoc's rule: the opening $ must be followed by a non-space, and the closing $ must be preceded by a non-space and not followed by a digit. A letter directly before $ and a digit after it (R$5, US$10) marks a price. $x^2$ still renders as math; single-dollar math that spans lines now renders as text.
  • Dollar amounts no longer render as math. In text such as “$5 and $10” or “R$5 or US$10”, the span between the two dollar signs turned into glued italic formula text. Single-dollar math now follows Pandoc's rule: the opening $ must be followed by a non-space, and the closing $ must be preceded by a non-space and not followed by a digit. A letter directly before $ and a digit after it (R$5, US$10) marks a price. $x^2$ and $$…$$ still render as math, and code, URLs and HTML keep their dollar signs as typed.
  • Messages sent during a run no longer all turn into “Not sent: it may have been sent.” After several prompts reached OMP in quick succession (for example automatic PR-update prompts), every later message to that session could fail this way while OMP still showed it, until an idle send restarted the session's OMP process. Now, at the end of each run and before an idle send, the web server asks OMP whether everything it accepted has settled, and only then trusts new messages' echoes again, without restarting OMP. Sessions shared through a host daemon recover too. Messages already marked uncertain stay that way and are never resent.
  • The file viewer highlights more file types, and you can pick the language. .diff and .patch files now show added and removed lines in color instead of plain text, and so do .proto, .ini/.cfg/.conf, .mk, .kts and .cc/.cxx/.hh files. .tsx/.jsx files now color JSX tags, and .scss files use the SCSS grammar. For any other file, the language menu in the source view's toolbar applies a grammar by hand (or plain text); it resets to Auto when you open another file.
  • Delivered messages leave every shared-server chat's pending list. When another web-server process delivers a message from the shared outbox, open chats now receive its updated state and delivery confirmation without switching sessions or reloading. Startup retries resync existing chats, observer failures cannot interrupt delivery or other observers, and pending, failed and delivered messages retain their historical session identities through repeated native moves. Per-session revisions prevent unrelated database writes from resending unchanged chat payloads.
  • A pending message is replaced, never dropped. With an OMP that reports supportsClientMessageIds, each message carries its outbox ID to OMP, and the pending row stays until that exact message, queue entry or explicit withdrawal appears in the chat, even when another server delivers it or its echo arrives late. If OMP no longer has any trace of it (its process ended, or /new ran while it was queued), the row turns into Not sent: it may have been sent and can be dismissed. An older OMP is sent messages as before and their rows clear on delivery.
  • Anthropic stays visible when usage limits are unavailable. The Provider Usage panel keeps accounts that omp reports without usage data and marks them as unavailable rather than unlimited.
  • A reply that is still streaming looks live again. The streaming reply was rendered as if it had finished, so its live header and tokens-per-second badge never appeared and its copy and read-aloud actions showed while omp was still writing. It is marked as streaming again; a finished reply held until its saved copy arrives still renders as finished.
  • A delayed archive restore cannot overwrite newer archived work. Restore now pins the compressed source generation it staged and verifies an atomically captured archive entry before moving artifacts. If another server restored, updated and re-archived that session meanwhile, the stale restore is refused and the newer transcript and artifacts remain available. Captured archives remain discoverable by session ID before publication and after an interrupted restore, so held messages are not falsely failed as belonging to a missing session; orphaned restore claims report their manual-recovery path.
  • In-progress archive captures are evidence, not restore targets. Captures have unique keys and disabled Restore controls, so a capture and a newer archive cannot select the wrong session. Archive discovery rechecks names and source generations after reading to avoid missing a capture during concurrent replacement; a capture encountered while pinning still reports its restore-claim recovery path.
  • Unreadable archive directories no longer look empty. Access and disk I/O errors retain affected outbox messages instead of proving their session absent. A corrupt or unreadable local archive is reported as one unavailable host in the archive browser, without hiding healthy linked hosts.
  • Archived attachments are not mistaken for session archives. Discovery excludes each archive's paired artifact tree, so arbitrary uploaded .jsonl.gz filenames cannot make the host's archive unavailable. Genuine nested and hidden project archives remain discoverable, including during restore capture.
  • Restore conflict checks require complete active-session discovery. Unreadable session roots, project directories or headers, projects disappearing after enumeration, and relevant symlinked session paths now block restore with 503 instead of hiding an active session and permitting duplicate IDs. Normal session browsing and its caches are unchanged; older daemons must update/restart before they can participate in the stricter restore preflight.
  • The model picker shows the new model at once. Picking a model in an existing chat updates the composer immediately, as the effort picker already did, instead of waiting until omp has restarted with it. A refused pick is dropped, an error notice explains why, and the composer refreshes the running model when possible.
  • The dictation waveform recovers when the browser suspends its audio. If the browser suspends or interrupts the meter's audio context mid-recording or during a pause, the recording resumes it, so the bars follow your voice again instead of staying flat while the recording itself works.
  • Provider cards no longer look clickable. Hovering or tapping a provider's non-interactive card no longer accents its border. Configure and Manage keep the neutral secondary-button styling used elsewhere in Settings; the clickable Browse all providers card keeps its hover feedback.
  • The retry banner keeps its cancel button on-screen on mobile. Retry details wrap within the composer, including long provider errors, instead of pushing the Abort retry button past the side of the screen. Long details scroll within a bounded area, and the controls reflow in narrow chat columns.
  • Windows host updates allow more time for safety checks. The process inventory gets up to 20 seconds to start and finish on a busy machine, instead of five. Shared installations and failed or unreadable inventories still block the update before the daemon is stopped.
  • Copied uploads start retention at the send time on Windows too. When a hard link cannot be used or its timestamp cannot be changed, the host writes a fresh exclusive copy instead of inheriting an old staging timestamp. Failed copies remain retryable without replacing unrelated files.
  • Windows servers can manage pools of Unix workers. Pool configuration and clone paths use the worker's path rules, not the web server's OS. Creation journals retain their synced-file, atomic-rename commit on Windows without requiring unsupported directory fsync.
  • Session HTML export supports Windows OMP launchers. Export uses the same .cmd and .bat handling as RPC sessions and version checks.
  • Git status and pool preparation work under Windows short paths. A workspace opened through an 8.3 short name (such as C:\Users\RUNNER~1) or with backslashes no longer loses its Git changes, and pooled preparation no longer rejects its own clone as "not an independent repository". Repository roots are located relative to the workspace instead of comparing Git's spelling of the path with the caller's.
  • Newly sent uploads survive an overlapping retention sweep. The host refreshes the staged file's timestamp before making its hard link visible, for attachments and Files-panel uploads alike, so cleanup cannot mistake a just-sent file for an expired one. A Files-panel overwrite of an expired file can no longer be deleted by a sweep that inspected the file it replaced.
  • Windows upload cleanup respects each daemon's retention policy. Shared temporary roots now keep each daemon's attachments in its own subfolder, so a seven-day policy cannot delete another daemon's “Never” uploads, even with copied session UUIDs. Existing flat fallback files remain readable and are left untouched because their owner is unknown.
  • Credential copies and config .env files stay private without blocking independent uploads. The file viewer and upload preflight/overwrite deny editor swaps and backups such as .agent.db.swp, agent.db~, .models.yml.swp and models.yml-backup only directly in omp's actual agent directories (including profiles). .env and .env.* are also denied at genuine default/profile config roots and agent directories, including their symlinked real targets. Same-named session uploads, workspace files and package assets remain readable and replaceable.
    These checks run on the file's owning host, including remote hosts. An upload batch containing a protected target is refused before any file in that batch is published, and publication rechecks each target before replacing it.
    Profile .env files stay private even when the profiles directory cannot be listed. If credential ownership metadata is unreadable, the file service returns 503 rather than guessing that an off-tree alias is safe; restore the daemon user's access to that metadata to resume ordinary file access. Uploads also refuse credential targets behind dangling, relative or chained configured symlinks, without creating the missing credential file.
    Ownership checks also retain native file identities when POSIX filenames cannot be decoded as UTF-8. On macOS, missing credential targets remain protected through filesystem case aliases, without creating calibration files.
    A failed lookup of the requested path stops that request, even if the filesystem recovers immediately afterward. It cannot fall through to a later successful read of credential bytes.
  • Table charts show every numeric column at once. Columns that share a unit are drawn as grouped bars with a legend, instead of one column at a time behind a selector, and columns with different units each get their own chart in the same panel. Rows are labelled with the first descriptive column, so an index column such as # is no longer used as the label or charted. Values that start with about, approx., approximately, around or roughly are read as estimates instead of making the column unchartable. The axis labels now line up with the bars, and the SVG export contains all of the charts.
    Charts remain readable on browsers without CSS subgrid. Tables with more than five same-unit measures are split into additional charts so no legend repeats a colour.
  • !! shell commands can now exclude output from model context. OMP 18.18.1 introduced the required RPC capability; ompweb checks the session process's startup declaration before executing, rather than trusting the host's installed version. Updated host daemons report that capability for shared sessions, including after reconnects and server restarts. Output remains visible and saved in the transcript. Older runtimes or daemons that cannot report support refuse exclusion; update the runtime and host daemon as needed, then reload the session.
  • Shell commands visibly change the composer. ! uses a monospace terminal field with a red, root-style warning because its output enters model context; !! uses a quieter terminal treatment. The distinction remains visible while focused and disappears when returning to ordinary text or attaching files. The warning does not grant root privileges.
  • Shell mode matches what Send will do. Inline text attachments remain prompt attachments even when the draft begins with ! or !!; their contents are never interpreted as shell commands. Rejected sends restore the original text and file attachments separately, so retrying does not turn the prompt into a shell command. During an active run, unattached shell commands say Run shell command rather than Queue or Steer. Shell-mode font changes also resize the composer when attachments are added or removed, and the exclusion label explicitly states that output remains saved in the transcript.
  • Recovered prompts and attachment-backed drafts preserve their input mode. Prompts remain model input even when their text starts with ! or !!, including after navigation, session identity moves, rejection, and page reload. Drafts keep this provenance even when memory-only attachment payloads disappear on reload. Clearing the recovered content, including removing its final image from an otherwise empty composer, starts fresh input so a new shell command works normally. Empty recovery results never change newer input.
  • History prefills and unfinished uploads keep prompt intent. History Edit, edit-and-resend Fork, and refused interrupt/reply prefills remain model input when their omitted attachments once prevented shell execution. Existing user drafts keep their own intent. Draft snapshots capture every live attachment before filtering unfinished upload payloads, so uploading or failed file chips cannot turn a prompt into a shell command after navigation or reload.
  • Side questions look different before you send. Typing /btw tints the composer and shows Send, even during a run and when the usual submit action is Queue or Steer. Side questions are sent immediately, never queued.
  • Finished background tool calls no longer show up as running. A bash or task call that omp moved to the background kept reporting progress after it returned, and ompweb counted each report as a running tool. Opening the chat mid-run then listed those long-finished calls as empty running rows below the transcript until the next event, and an open chat could briefly show a finished call as running again. Calls that returned before an ompweb server restart can still show this once.
  • Background Sync retains permanent rejection states. A rejected held message, including one from a changed installation scope, is saved as Not sent rather than left as Sending. It is not rebound to new credentials, and a late refusal cannot recreate an entry another sender already accepted.
  • Pending messages protect newer drafts and delivery intent. A delayed storage acknowledgement does not clear newer input, even if an edit restores identical text. Same-time submissions keep durable enqueue order; duplicate browser IDs and unrelated successful HTTP responses cannot overwrite or acknowledge a held message. Delete confirmations distinguish removing tracking from cancelling work omp may already have received.
  • Outbox recovery remains live through storage and worker changes. Storage initialization retries after transient failures, observers attach when an explicit command starts a ready session, and Background Sync verifies worker capability and durable transaction completion. Server-held capacity is bounded across sessions; overflow stays saved in the browser until capacity is available.
  • Slow browser links keep only the latest pending outbox snapshot. Repeated held-message changes no longer fill the shared connection with redundant large snapshots. Native event ordering and the existing overflow recovery remain intact.
  • A transient browser-storage read failure no longer disables sending until every tab closes. Later outbox operations reload the retained messages without restarting the worker, preserving their order and duplicate protection.
  • Completed attachments survive ordinary re-login. The browser retains an upload-specific completion proof with its pending message, so renewed login cookies do not lose file ownership continuity. Accepted entries keep private per-upload authority across server restarts. Preparation stays bound to its original completion generation, so cancellation and ID reuse cannot substitute another file's bytes. Existing upload-owner, host/session and expiry checks remain intact.
  • Pending-message tracking preserves browser tools and dictated drafts. An internal delivery observer no longer prevents another open app tab from handling a browser tool, and an earlier prompt's asynchronous failure cannot mark a successfully queued follow-up as “Not sent.” Automatically sent or queued dictation clears its submitted text after durable acceptance without clearing newer input, including an identical-text retype.
  • A completed or refused local command cannot hide a later missing response. When an outbox attempt finishes without starting a turn, its saved-history boundary no longer leaks into the next turn's recovery checks.
  • Pending messages follow native ownership moves immediately. The confirmed identity change durably retargets held and failed entries before another outbox action, retaining original-ID replay receipts across server restarts. Ordinary branches and session switches stay separate.
  • Deleting a held message cannot be undone by late preparation errors. A refused or retryable startup result cannot revive the retired entry or send its emptied tombstone.
  • Offline remote chats remain discoverable after a web-server restart. An empty host cache or failed session-list refresh no longer turns a valid remote chat into a permanent “Session not found” error just because the local daemon is online. Pending messages stay held and reach their owning host once it returns; confirmed missing sessions still fail normally.
  • An agent's open_file now opens local:// files. open_file with local://note.md (or local://nested/name with spaces.md) opens the file from that session's own local:// directory in the file viewer, where it used to open a tab that failed with a 403. The URL is read as omp reads it, so a name with a percent such as local://100%25.md opens 100%.md. The file is looked up for the session that asked, even when you are viewing another one, and is read, downloaded (from the file panel's header button too) and live-watched like any other file; a path swapped for a link out of the directory after the file was opened reports nothing of the other file. A file that does not exist, a directory, a URL that leaves the directory (.., an absolute path, a symlink out) and one that names a session that does not exist are refused with a message to the agent instead of opening an empty tab, and a lookup the server never answers ends the call with an error after 30 s instead of leaving the agent waiting. A session on a linked host works the same way: that host's daemon resolves and confines the path on its own machine. A host daemon that predates this lookup answers "update or restart the ompweb host daemon" instead of opening an empty tab.
  • Opening a local:// link refuses omp's own secret files before creating a tab. The lookup uses the same credential policy as the file viewer, including symlinked agent directories and credential files, so the agent receives an access error instead of a successful “Opened” result followed by a failed tab. Unrelated session artifacts with credential-like names still open.
  • Session-local tabs keep reading the owning artifact. A workspace file mirroring the artifact's absolute pathname no longer shadows it, and a completed symlink replacement outside the session's local directory is refused even when a broader agent-directory root is browsable.
  • Delayed artifact lookups respect the current conversation. A file request that finishes after its chat was left no longer opens over the new view; cross-session requests ask for confirmation, including while Settings is open. A live session addressed through an old ID uses its current temporary artifact root on Windows.
    Canceling an outstanding file request aborts its lookup and prevents a late tab or confirmation from appearing. Cancellation stays scoped to that request and its original session, including after a confirmed session-ID move.
  • Removing an upload while offline still deletes it from the host. A file you remove (or a finished upload you discard) is now deleted from the host's staging area once the browser, the server and the host can reach each other again, even if the browser connection dropped or the host was offline when you removed it. As a backstop, a running host now deletes staged files older than a day every hour, not only when it starts or receives a new upload, which also covers a removal that ompweb lost because it restarted while the host was offline.
  • Sent-upload retention reaches hosts already linked at server startup and retries live-link failures. An attachment whose timestamp cannot be updated is copied so it does not expire before its selected retention period; unreadable cleanup folders are reported in host logs.
  • ! commands you run have their own look. A shell command you ran with ! now shows as a terminal-style card with a $ command prompt line, instead of the same row as the agent's tool calls. A failed run shows its exit code (exit 1) or cancelled, and a !! run is tagged local. Click the card to show its output. Long pending commands wrap even on narrow screens, switching to a different run resets its expansion, and screen readers can identify successful runs.
  • The Explorer shows every file, including build output and .git, with no switch. node_modules, .git, dist, build, target, caches and .pyc files are no longer hidden by name. A folder's contents load only when you expand it. This replaces the Show hidden items switch from #378, including its toolbar button and saved preference. Symlinks show their raw target (name → ../target); an outside-root or dangling link stays visible but cannot be opened or expanded from the tree.
    File search and @ mentions follow .gitignore in a Git repository. Outside Git, or when Git is missing or fails, the bounded fallback includes regular files without name exclusions, including ignored files, but skips symlink entries to both files and directories.
  • File search stays bounded and leaves the host responsive. The non-Git fallback reads directories asynchronously instead of blocking other daemon requests for the whole scan. It still queues and reads at most 50,000 directories and reports truncated results at its limits. Blocked symlink tooltips explain both outside-workspace and missing targets in every language.
  • Explorer confinement upgrades without changing older daemon contracts. The original boolean listFiles operation remains available to older web servers. The Explorer uses the separately advertised listFilesInRoot capability; a daemon without it returns an explicit upgrade-required response rather than an unconfined listing.
  • Reconnecting uploads no longer leave Send blocked by a hanging retry. The five-minute deadline stops an in-flight retry as well as its back-off, keeping staged bytes for Retry. A connection lost midway through an HTTP response resumes automatically, and tooltips identify network failures instead of showing only “Upload failed”.
  • Files named agent.db or models.yml open again unless they are omp's own. The file viewer refused those names anywhere under ~/.omp, so an uploaded agent.db could not be opened from its link. It now refuses only the files omp keeps in its agent directory (and a profile's), with their SQLite sidecars, backups and models.yaml/models.json. Both symlinked agent directories and individually symlinked credential files keep their real targets protected, including targets with different names; unrelated uploaded copies still open.
    Hard-linked aliases of those owned secrets are denied too, by comparing device and inode identity. Independent copies still open, even with identical contents, and upload preflight and overwrite cannot modify a credential through an alias.
    On Windows, alternate data streams attached to protected files are denied too, including aliases and attempts to create a new stream. Independent streams remain available, and literal colon-named files on POSIX keep their ordinary meaning.
  • Uploads no longer leave submitted messages or restored pages stuck. A slow attachment copy keeps its session alive even after you switch chats. Commands that change Advisor wait until that preparation is dispatched before they may replace the session's child. Returning through browser Back/Forward shows Retry for an interrupted upload instead of leaving Send permanently blocked.
  • Attachment retries no longer accept missing files or stale upload errors. An attachment removed or replaced by a directory is reported as missing before it is marked complete. Failed receipt writes leave the staged file available to retry without creating a duplicate, and cancellation cannot be undone by an attachment still finishing. Replies from earlier upload attempts no longer fail a newer Retry. Retrying also waits for failed-write cleanup, so an old attempt cannot delete the new attempt's staged files.
  • A conflicting upload start no longer interrupts the original file. Reusing an upload ID with another filename or MIME type is rejected before changing the active transfer, so the original upload continues normally.
  • PDFs stay file attachments when the browser omits their MIME type. Filenames ending in .pdf (any case) no longer have their raw contents inserted into the prompt as text.
  • A ! shell command sent during a run now runs, as in omp's terminal. It was queued as a plain message for the model instead. It now runs right away beside the turn, and its output shows below the conversation until omp adds it to the history, which omp does when a later prompt starts. A steer sent while such a command runs still steers the turn, and Stop stops the turn first, as Esc does in the terminal. Python ($) commands still run only in omp's terminal: omp's RPC mode has no command for them yet.
  • A message sent just before switching chats is no longer held back. When the session had to start omp first (it had been idle long enough for its process to stop), switching to another chat during that startup cancelled the send and put the text back in the composer. The message is now sent, as it already was for a new chat.
  • Sending a command detects a silently broken browser connection sooner. Queued messages and steers initially reconnect after 5–6 seconds without delivery-acknowledgement progress, rather than waiting for the 30-second silence check. This also detects a dead uplink while replies still stream down. Recovery uses the existing ordered replay and deduplication; slow command results do not trigger retries. Repeated delivery stalls increase the deadline up to 30 seconds for slow links. Server restarts and expired replay windows retain the existing explicit "may have run" error rather than risking duplicate actions.
  • An open chat now picks up a run that started after it loaded. When a run started from another tab or device, or from the same chat opened again while its message was still starting omp, the sidebar showed the session running but the chat stayed idle until a reload. The chat now attaches to the run as soon as the sidebar shows it.
    Running updates received during a state check are retained, and compaction controls immediately reflect the attached run. Compaction started in another tab also releases its controls when it finishes while you are reading a historical branch, without replacing that branch. A late state check cannot take over a command this chat has started or bring back a shell command that has finished or been stopped. The chat waits for its first load before attaching, so the branch navigator never comes up empty, and it attaches once its own send, ! command, Edit or compaction is done if a run started meanwhile; a failed check is retried. A compaction you start keeps its Stop control until it ends, even when the session's omp is still starting.
  • A finished compaction no longer reports "The agent stopped without returning a response". When a chat caught up with a session that was only compacting (started from another tab, or while the chat was reconnecting), it treated the compaction as a prompt run, showed it as running, and reported an empty reply once the compaction ended. A compaction now shows only its own Stop compaction control; a compaction during a run still keeps that run going.
  • The dictation waveform no longer stays flat while you speak. The meter's audio context was created only after the microphone opened; when that took longer than the click's activation window, the browser kept the context suspended and the bars showed silence even though the recording and transcription were fine. The context now starts inside the click.
  • Streaming replies use about a sixth of the bandwidth. ompweb now asks omp (18.13.2 or later) for only what changed in a streaming reply instead of the whole reply several times a second, and the browser rebuilds the reply itself. In one measured reply the live stream fell from 232 KB to 91 KB, and the reply updates themselves from 178 KB to 30 KB, so replies keep up on slow mobile links. A tab opened mid-reply, a reconnect and a restarted server still show the whole reply. An older omp keeps sending whole replies and works as before.
  • The host link is now one WebSocket, with fewer requests and large results sent in the same connection. A reverse proxy in front of ompweb must forward WebSocket upgrades on /api/hosts/link (see the README's hosts section). Update ompweb on the server first, then on its hosts: a host linked with an earlier build of this release shows needs upgrade until it is updated.
  • The browser connection is now one WebSocket that survives drops. Settings → Interface & Behavior → Browser connection now defaults to the WebSocket link: one connection per browser that carries live updates and chat commands with the same delivery as the host link. A large event no longer delays a command's answer, a slow answer never resets the connection, and after a short drop (up to 2 minutes) the connection picks up exactly where it stopped, without reloading the chat (if more than 32 MiB of updates piled up meanwhile, it reloads the chat instead). Longer outages and server restarts reload as before; a command that cannot be confirmed still says it may have run. The proxy must forward WebSocket upgrades on /api/events/link; a browser whose proxy refuses the upgrade falls back to server-sent events on its own. To use server-sent events + HTTP all the time, switch the setting back to it in Settings. The earlier experimental WebSocket choices are gone and read as the default.
  • ompweb now runs on Bun instead of Next.js. The npm package brings its own Bun, so installs and updates work as before, with no change to URLs, ports, settings or the password gate. OMP_WEB_DEV_ORIGIN is removed: it only configured the Next development server. npm run build no longer interferes with npm run dev. The skills.sh search now gives up after 10 s and falls back to the skills find CLI instead of waiting on a stalled skills.sh.
    • If you start ompweb from a terminal (ompweb or npx), start it again once after the in-app update to this version. The launcher that is already running applies the update and then tries to restart the server with Next, which the update removed, so the server does not come back. systemd, launchd and the tray start the new launcher and are not affected.
    • Browsers older than Safari 16.2 or Chrome 111 lose the colours ompweb mixes with color-mix() (tints, borders and focus rings), which no longer carry a solid fallback. Those browsers were outside the Next.js build's targets too.
  • The service installers set up the host daemon too. ompweb systemd install, ompweb-launchd install and the Windows tray install now register ompweb host as a second unit (ompweb-host) next to the web service, restarting it after a 10 s delay so it can wait for the web server to write its host link on first install. uninstall and status cover both, and the tray menus show the daemon state.
  • Forking a session or editing a message asks first. The Fork button under a message and the Edit button under your own message now open a confirmation dialog, so a stray tap while scrolling no longer creates a new session or rewinds the current one.
  • Expanded thinking blocks no longer collapse when a step finishes. With "Expand thinking blocks by default" on, a thinking block opened while streaming, then moved into a closed Process details row as soon as the step committed. Thinking now stays out of the fold while the setting is on.
  • In-app updates work when ompweb runs as a systemd service. The update worker ran inside the service's cgroup, so stopping the server killed the worker too: nothing was installed, the service could stay down (Restart=on-failure does not restart a clean SIGTERM exit), and every page load reopened the update dialog at "Stop ompweb-managed sessions". The worker now runs in its own transient scope (systemd-run --scope) and stops, updates and starts the unit through systemctl, starting it again if the install fails.
  • An update whose worker died is reported as failed. A running update whose worker process is gone, or whose lease has expired, now becomes a failed attempt the dialog can show and clean up, instead of waiting forever.
  • Upgrade ompweb before its hosts. The web server links a host daemon one link protocol version older than its own, so a host keeps running sessions until its ompweb is updated. After a web restart the server adopts that daemon's live runs, as it does a current daemon's, without resuming them; only a large message from such a host still arrives whole instead of in pieces, which can stall that host's link on a slow connection. A daemon older than that, or newer than the web server, is still asked to upgrade.
  • The Provider Usage header stays put while its list scrolls. Scrolling a long provider list in the sidebar also scrolled the section header (title, window toggle, refresh) out of view, on desktop and mobile. Only the list scrolls now.
  • Android system bars match the theme's top bar. The browser theme color, which tints the Android status bar, now uses the top bar's color instead of the page background, so a light theme no longer shows a white status bar above a tinted top bar. An installed app also reports the current theme's color in its manifest, as both its theme and background color, which Chrome uses for the navigation bar (Chrome 153 and later). Chrome applies it only when it next checks the installed app for manifest updates, so after a theme change the navigation bar can keep the previous theme's color for a while; tap Request update for the app in chrome://webapks to apply it sooner.
  • No shadow smudge at the top-left of light themes. The hidden "skip to content" link sat just above the screen, and its drop shadow showed through onto the top bar. The shadow now appears only while the link is shown.
  • On Android, omp-web draws behind the navigation bar instead of leaving it black. The phone sidebar and file panel were anchored to the bottom of the screen and padded with the bottom safe-area inset, which Chrome treats as a reason not to draw edge to edge, so it painted the navigation bar itself (black in the installed app, and flashing while the sidebar opened). The panels are now sized by height instead, so the page's background shows under the navigation bar.
  • No more blank space above the composer in long chats. In a chat with many messages, the position rail on the right could be taller than the message area. That happened most once Tasks or Subagents were expanded above the composer. The rail then pushed the messages up and left an empty band above the composer, which grew with each panel opened. The rail now fits the message area and scrolls instead, keeping its active marker visible when panels expand or collapse.
  • No blank band above the composer on phones either. Scrolling to the newest message could also shift the message area itself up, leaving an empty band above the composer until the page was reloaded. Scrolling can no longer shift the message area (Safari 16 and later; older Safari keeps the old behavior).
  • Sending to a session another omp process has open no longer leaves a dead fork. When omp moved the resumed session to a new file during startup, the tab kept the old id, the event stream failed with "Failed to connect to the agent event stream", the prompt never went, and the forked session was reaped two minutes later. The requested id now stays routable alongside the new one.
  • Changing Advisor keeps a moved session on the right conversation. A tab still using the pre-move session ID now restarts the current session file on its owning host when toggling Advisor, instead of reopening the original file, selecting another host’s copy, or returning “Session not found” if the original was removed.
  • Workspace profile names are validated before they can break sessions. The profile inputs show the naming rule and reject invalid names, and the API rejects invalid profiles and reserved launch arguments. Existing invalid profiles and reserved arguments remain visible in the workspace launch configuration; an updated host daemon refuses to start a session until they are corrected or cleared, instead of silently switching profiles or passing an orphan argument. Update remote host daemons as well: older versions do not enforce this spawn-time check.
  • A failed run no longer shows its error three times. The duplicate "Request failed" toast is gone. A non-quota error already shown on the current run's assistant message does not also need a shelf notice; standalone errors and errors whose message cannot be confirmed remain visible in the shelf, even after partial output. If the matching assistant message is saved later, it replaces only that run's duplicate notice. Quota handling is unchanged and may show both an inline banner and a shelf notice.
  • send_to_session with delivery: "followUp" queues the message. It failed with "Unsupported command: followUp" instead of queueing the message after the target's running turn.

Upgrade

npm install -g @kahme247/ompweb@0.7.0

Full changelog: v0.6.0...v0.7.0

omp: ompweb-dev/oh-my-pi v18.20.0-int.202611131515.g11d9dc7