Toolchain Updates (background checks + native-installer notifications)
How Omniscio keeps the command-line tools it depends on up to date: a background version check, a one-click Update that runs that tool's own update command in the background on your computer, and the silent npm path kept for the pure-npm tools. Covers the two settings that control it, which tools never update silently, how a stuck install reports itself, and the inbox cards with a one-click Update now when Claude Code, Codex or another recommended tool falls behind.
What it is
Omniscio periodically checks whether the CLI tools it depends on (Claude Code, Codex, gh, git, …) have newer versions available, then makes it easy to update each one yourself, outside Omniscio. The core model (2026-06-29): every recommended tool you have installed gets a one-click Update that runs that tool's own update command in the background on your computer — Omniscio installs nothing in-place for this path, and since 2026-09-30 no window appears while it runs. The command is derived from how each tool was installed (TOOL_PROVENANCE): npm tools → npm install -g …, winget tools (git, github-cli) → winget upgrade …, Go tools → go install …@latest, GitHub-release tools → their own installer, and Claude Code → claude update against the actual binary Omniscio runs (or npm, if that's how it was installed). Claude Code used to sit in the silent-npm bucket, which ran npm install -g against a copy that often wasn't even on your PATH — so the Claude Code you actually use never moved. That was the bug; it now updates the real binary, in a background run you trigger. One convenience is kept for the pure-npm tools (playwright, agentmail, repomix): the Auto-update Tools toggle (off by default) silently npm install -gs them in the background when you turn it on, since npm needs no admin and that path has always worked. Every other tool — claude-code, codex, git, github-cli and the rest of the notify-only list — is never silently updated; it always notifies, and your click runs the update in the background. A second toggle, Tool update reminders (toolUpdateNotificationsEnabled, default off), is the master switch for the proactive method — the scheduled background version check AND the single aggregated "N tools can be updated" nudge. Leave it off (the default) and Omniscio won't nudge you; the one-click Update buttons and the Connected Tools panel's on-demand "Check for updates" stay available so you can update by hand anytime. The scheduled check runs when EITHER this toggle or Auto-update Tools is on (so the silent-npm path keeps its detection with reminders off); when only the update-card toggles (below) are on it still runs, scoped to the tools those cards cover; only with all four off does Omniscio stop background-checking entirely. Turn it on to opt into the proactive check + toast. Periodic checks run at startup + every 6 hours on a fixed cadence (module constants, not user-facing settings).
Where to find it
Everything lives in Settings → Connected Tools: the Auto-update Tools, Tool update reminders, Update alerts for Claude Code and Codex and Update alerts for your other tools toggles (under Update settings), a Connected Tools panel carrying each tool's one-click Update button and an on-demand Check for updates, and a Developer section with Run check now, Clear cache, and Simulate update. The aggregated N tools can be updated nudge opens the Tools view, which is where those Update buttons live.
How it behaves
How to use it
- Default behavior — nothing to configure.
autoUpdateToolsis off by default; turn it on and the pure-npm tools (playwright,agentmail,repomix) install silently in the background. The notify-only tools (claude-code,codex,git,github-cliand the rest) have no silent path — they run their own updater (and, for winget/brew tools, sometimes need admin), so they're never auto-installed regardless ofautoUpdateTools; each surfaces a one-click Update instead, plus an inbox card when it falls behind (below). Whether anything toasts is governed by the master kill-switch below: out of the box (toolUpdateNotificationsEnabledoff) updates stay quiet and only surface in Settings → Connected Tools and the Tools view. - Silent installs are opt-in. With Auto-update Tools off (the default), npm updates show an aggregate info toast with Update + Review buttons instead of installing silently — but only when notifications are on (see step 3) — and the npm trio gets update cards like every other tool. Turn it on in Settings → Connected Tools to install them silently instead. Native-bucket behavior is unaffected by this toggle: it never had a silent install path, and its toasts remain gated by
toolUpdateNotificationsEnabledeither way. - Default is silent — opt into popups. Out of the box, Omniscio suppresses every "update available" toast (npm aggregate AND per-tool native). Background detection still runs — updates appear in Settings → Connected Tools where you can install them by hand — but nothing pops up to interrupt you. To opt in, Settings → Connected Tools → toggle Tool update reminders on. Independent of
autoUpdateTools: with auto-update on, npm tools still install silently in the background regardless of this toggle; flipping notifications on just adds the "update available" toasts back for the cases where auto-update can't act (npm subset whenautoUpdateToolsis off, native subset always). - Update a tool in the background. When a notify-only tool is behind, or when you click Update on any tool in the Tools view, Omniscio runs that tool's own update command in the background on your computer — no window opens:
claude updatefor Claude Code (against the real binary Omniscio runs),winget upgrade Git.Git/winget upgrade GitHub.cli(Windows) orbrew upgrade git/brew upgrade gh(macOS) for the native tools, andnpm install -g …/go install …@latestfor the rest. A hand-installed tool (RTK, gog) opens its download page instead. Bun is updated through whatever installed the copy on your PATH, because it arrives from npm or its own installer as often as from winget: an npm install runsnpm install -g bun, a winget onewinget upgrade Oven-sh.Bun, a Homebrew onebrew upgrade bun, and anything else Bun's ownbun upgrade. What the updater prints goes to Omniscio's log file rather than to a console you have to close, and a click that reaches an updater says "Update started on your computer — you'll be told if it doesn't finish" so it is never silent. The card that was clicked goes with the launch, and the follow-up watch clears the row itself once the new version shows up — or reports, in its own card, that the update did not take. Every Update button says what happened: the update started, an update for that tool is already running, a download page opened, or nothing could start. - Recover from EBUSY. If a silent npm update (
playwright,agentmail,repomix) runs while another process holds a lock on the tool's files, npm fails withEBUSY: resource busy or locked. Omniscio'sINSTALL_ERROR_PATTERNSsafety net translates that to the friendly message "A process is using this tool. Close any running sessions or terminals using it, then try again." (tool-agnostic) instead of showing raw npm output. Close the offending session/terminal and retry from Settings → Connected Tools. - Developer tools. Settings → Connected Tools → expand Developer to run a manual check, clear the update cache, or simulate a fake update without waiting for a real remote release.
Update alerts for Claude Code and Codex
When a real check finds Claude Code or Codex CLI behind its latest release, Omniscio puts one durable card per tool in your inbox — "Claude Code needs an update", naming the installed and latest versions — with a one-click Update now. The button runs that tool's own updater in the background on your computer, exactly what the Update button in Settings → Connected Tools does; from your phone it still runs on the computer, and the card says so. Omniscio never starts the update itself. Pressing the button is the answer to the card, so the card goes as the update starts — it does not sit there still asking for the version you just ordered — and the toast says you will be told if the update does not finish. If the update really does not take, a different card appears: "<tool> update didn't finish", naming the version you are still on, with a Try again button that runs the same updater. That verdict is never a guess from a timer: it is raised only once the update process has actually finished and the tool is still behind, so a slow install is never reported as a failure, and a version that could not be read reports nothing at all. Make sure nothing is still using the tool — for Claude Code, close running sessions — before trying again. Each new version is announced once: a card you dismiss stays quiet for that version, a newer release raises a fresh card, and the card also withdraws on its own once a check proves the tool current. A tool that is offline, mid-install, not installed, or whose version cannot be read raises nothing and withdraws nothing. The cards are on by default and independent of Tool update reminders; Settings → Connected Tools → Update alerts for Claude Code and Codex turns them off, and a brand-new install is not shown them during its first day. Developer → Simulate update for Claude Code or Codex raises the real card too (with no version watermark, so it clears on the next real check or when you dismiss it).
Update alerts for your other tools
The same card covers every other tool Omniscio recommends and can check reliably: Git, GitHub CLI, OpenCode, Pi, portless, agent-browser, pnpm, yt-dlp, Bun, gws, kubectl, RTK and gog — plus Playwright, AgentMail and Repomix while Auto-update Tools is off (with it on, Omniscio updates those three itself, so it never cards them). It works exactly like the Claude Code and Codex card: one card per tool per version, a one-click Update now that runs the tool's update in the background, the card coming down as the update starts, and the same "update didn't finish" card with a Try again button if it does not take; it also clears itself once a check sees the new version. It has its own switch, Settings → Connected Tools → Update alerts for your other tools (on by default); turning either switch off leaves the other family exactly as it was. RTK and gog are installed by hand, so their card says so and its button reads Open download page. A card only appears when the update can really be delivered: a release with no build for your computer never raises one, and a release for a tool updated through winget or Homebrew (Git, GitHub CLI, yt-dlp, Bun, gws, kubectl) waits a day so the store has it before the card offers it. A copy that came from somewhere else — kubectl bundled inside Docker Desktop, yt-dlp installed with pip — gets no card either, because the winget or Homebrew update the button runs cannot reach it (Docker Desktop updates its own kubectl when Docker Desktop updates); a card already up for such a copy is taken back. Bun is the exception: its button follows whatever installed your copy, so it keeps its card. A tool whose copy on your PATH belongs to a project gets no card and no Update button either: when you run Omniscio from its source code, the project's own pinned pnpm and its Playwright come first on the PATH, so their version is the project's to set and an update would only replace a different copy that nothing runs. Docker, Google Cloud SDK, Tailscale, AWS CLI, Python, FFmpeg and the security tools get no card, because Omniscio cannot read their newest version reliably. Developer → Simulate update raises the real card for any of these tools too.
For agents
How it works
The service is toolchain-update-service.ts. On startup + every 6 hours, doCheckForUpdates() fetches the latest version for each tool in VERSION_SOURCES (npm registry for the npm tools — claude-code, codex, playwright, agentmail, repomix, opencode, pi, portless, agent-browser, pnpm; GitHub releases for the rest — git, github-cli, yt-dlp, bun, gws, kubectl, rtk, gog). The isNativeNotify(id) predicate splits the result into two subsets — its membership comes from NATIVE_NOTIFY_TOOL_IDS — now every notify-only tool, fifteen of them — in src/shared/types/toolchain.ts (re-exported by src/shared/types.ts), the single source of truth (NEVER inline that literal — always import). claude-code moved here from the npm bucket in the 2026-06-29 universal-update change, so the silent npm path now covers only playwright, agentmail, repomix. When autoUpdateTools is on, npm updates flow into installAvailableUpdates() from toolchain-installer.ts for silent install; native updates and the npm subset (when autoUpdateTools is off) go out under a single TOOLCHAIN_UPDATES_AVAILABLE push so the toolchain-update store stays the authoritative list. If a silent auto-install THROWS (registry error, permission, network drop), the failed npm updates are re-emitted under that same TOOLCHAIN_UPDATES_AVAILABLE push as a manual "update available" (F114) — so a stuck tool never silently reads as current when auto-update couldn't complete; the next 6h detection re-attempts the idempotent install. The renderer hook useAppToolchainAlerts.ts now raises a single aggregated N tools can be updated nudge (no per-tool toast storm — the 2026-06-29 change collapsed the per-tool toasts into one) that opens the Tools view, where each tool's Update button invokes TOOLCHAIN_OPEN_NATIVE_UPDATE for the terminal path (or TOOLCHAIN_INSTALL_UPDATES for the silent npm batch). The master kill-switch toolUpdateNotificationsEnabled (default false) is enforced as an early return at the top of handleToolchainUpdatesAvailable — when not strictly === true, the hook drops both the npm-aggregate and native per-tool branches before any showToast call. The strict !== true check (rather than === false) ensures upgrading users whose settings have no persisted value (undefined) also default to silent — getSettings() returns the raw config without merging DEFAULT_SETTINGS, so a missing key is the common case for everyone who installed Omniscio before this default flipped. Completion toasts (handleToolchainUpdatesCompleted) are intentionally NOT gated, so the user still sees feedback when an install they triggered finishes. That handler (toolchain-handlers.ts openToolUpdate(), generalized in the 2026-06-29 change from the old git/gh-only openNativeUpdate) builds the per-tool command with the pure buildToolUpdateCommand (toolchain-update-command.ts) off TOOL_PROVENANCE, then launches it IN THE BACKGROUND through toolchain-update-launch.ts: the command goes through the canonical session spawn primitive (spawnCliChild), which gives it a hidden console plus the Win32 Job Object, and on Windows it is wrapped in cmd.exe /d /s /c "…" by the sanctioned producer because the builder emits bare names (npm, winget, bun) that only resolve once cmd has applied PATHEXT. It is deliberately NOT spawned detached — a detached child owns no console on Windows, so every console program under it allocates its own visible window. The updater's stdout/stderr and its exit line are mirrored into the app log, and the click's toast is the feedback. The tool/package id is validated by the builder's SAFE_UPDATE_ID allow-list before it touches the shell string (injection guard). The updateTool() IPC handler still services in-place updates for the npm bucket but explicitly rejects native tool ids with "updateTool no longer supports ${toolId} — use TOOLCHAIN_OPEN_NATIVE_UPDATE" so future callers can't drift back to the in-place path. A safety-net entry in INSTALL_ERROR_PATTERNS translates residual EBUSY: resource busy or locked npm errors to "A process is using this tool. Close any running sessions or terminals using it, then try again." (tool-agnostic so it stays accurate regardless of which tool fired the EBUSY) — see sanitize-install-error.test.ts for the full pattern catalogue. Periodic checks use startToolchainUpdateChecker() on a fixed cadence — the module constants CHECK_INTERVAL_MS (6h) and INITIAL_DELAY_MS (5min); there are no user-facing timing settings (the former toolchainCheckIntervalMs / toolchainInitialDelayMs / toolchainCheckAtStartup fields were removed as dead in RT-F007 — the service never read them). The scheduled runPeriodicCheck does the work periodicCheckScope() allows (fire is still recorded for cadence; fail-open on a settings-read error): 'all' when autoUpdateTools || toolUpdateNotificationsEnabled, 'alert-tools' when only the card toggles (agentCliUpdateAlertsEnabled, otherToolUpdateAlertsEnabled) are on (a sequential probe of the carded tools whose switch is on that keeps every other tool's cached entry), and 'none' only when all four are off — so a fully opted-out user stops background-checking while the kept silent-npm path still gets its detection; the manual "Check for updates" path stays ungated. Gate locked by toolchain-update-service.test.ts — the per-flag scope cases prove the ||. After every real check (and after clearUpdateForTool / the post-update reconcile), agent-cli-update-alert.ts syncs one inbox card per carded tool — agent-cli-update-available:<tool> for the agent CLIs and tool-update-available:<tool> for every other updatable tool, each family behind its own switch — raising only when the checker's hasUpdate is true and the version is newer than the tool's watermark (the checker holds a release with no build for this OS, and a winget/brew-delivered release for its first 24 hours, flags managedElsewhere — offering nothing — for a tool whose probe answered from a project's own node_modules/.bin (the projectLocal flag set by resolvesToProjectBin in toolchain-exec.ts), and never offers a winget/brew update for a copy on PATH that store did not install — toolchain-copy-owner.ts decides with the update terminal's own absolutePathOnPath + classifyInstallOrigin, a selfUpdate tool excepted, and the entry carries managedElsewhere), withdrawing when proven current or managedElsewhere, holding on 'unknown', and never carding the npm trio while autoUpdateTools owns it. Both families' update-agent-cli action (an exact Open download page row for the hand-installed rtk/gog, and a Try again row under each family's -failed: prefix) runs through the seam agent-cli-update-action.ts into the same TOOLCHAIN_OPEN_NATIVE_UPDATE handler, which answers what it started (background / docs / none / already-running) and refuses a second launch while toolchain-update-reconcile.ts's post-launch watch (which covers every updatable tool) is still watching that tool. A background launch also takes that tool's cards down and hands the watch the updater's exit; the watch raises agent-cli-update-failed:<tool> / tool-update-failed:<tool> — the same card in the same family, "update didn't finish", with a Try again button — only when that process has finished and a probe still reads the tool behind, and clearing a tool current withdraws both of its keys. All three Update buttons — the card, Settings → Connected Tools and the Tools view — turn that answer into a toast through one helper, tool-update-outcome.ts. The cards' rules are the agent-cli-update-alerts contract; the subsystem's parts and traps are the toolchain-updates map. UI settings live in ToolchainSettings.tsx — the Developer section exposes dev-only buttons for Run check now, Clear cache, and Simulate update. Design history: the file is named toolchain-update-v3.md because it documents what replaced an earlier "v3" deferred-queue model. v3 queued native-tool updates into a pendingToolUpdates settings field and ran a blocking startup splash to install them on next launch; that was removed in favor of running the installer right when you click, because winget/brew do that work themselves and the queue + splash machinery had migration and race-condition costs. The current "Option B" model has no queue, no startup splash, and no pendingToolUpdates settings field (a one-shot legacy migration in config-store/migrations/migrate-pending-tool-updates.ts drops the field on read). See postmortem § Section D (merged 2026-05-08 from toolchain-option-b-refactor-postmortem.md) for the migration write-up.
Related
- notifications-and-silence.md — toolchain toasts route through the same notification stack
- session-stuck-in-needs-you.md — when a session won't start, a missing/outdated tool is one of the diagnosis branches
Last verified 2026-10-03