Commit Graph

5713 Commits

Author SHA1 Message Date
brooklyn!
09fbc27a4a test(desktop): preserve currency whitespace in billing queries 2026-09-23 08:40:08 -05:00
brooklyn!
cc0388e236 test(desktop): assert locale-aware counts and timeline precision 2026-09-23 08:40:08 -05:00
Halldrix
121de845a4 fix(desktop): paint live rows under the warm-resume hold, suppress only the cached prefix (#117867) (#119552)
Co-authored-by: Halldrix <12357213+Halldrix@users.noreply.github.com>
2026-09-23 12:52:27 +00:00
Calvin Ng
ee94a6727f fix(desktop): stop background secondary reopens from re-resuming every tile
The Bot Mode relay drains every registered connection every 30s. The local
route is exempt from relay socket retention (#93594), so each tick dials a
fresh requestGatewayForAgent('local', ...) secondary and disposes it after
the RPC. From the second tick, openSecondary saw the scope in
openedSecondaryScopes, treated the dial as a backend reopen, and called
resetTileRuntimeBindings (added in 613822afff), which drops the runtime of
every tile without an owner route plus the delegate's stored->runtime cache.
Those tiles ride the ambient gateway, so every split pane re-resumed and
remounted its composer every ~30s: caret reset, layout shift, and a reverted
composer model pick on an idle Desktop.

A reopened secondary that is not the window's active scope now resets only
tiles whose ownerRoute names that route (resetRouteOwnedTileRuntimeBindings
plus a drop-only dropRuntimeBindings delegate hook). The active-scope reopen
keeps the window-wide reset, so the stale-runtime guard from 613822afff still
covers the tiles that actually live on that socket.

Refs #108088, #118856
2026-09-23 06:57:17 -05:00
teknium1
92e2e7cb6a Merge remote-tracking branch 'origin/main' into hermes/hermes-b802e898 2026-09-23 03:52:11 -07:00
Calvin Ng
ef2307dde3 style(kanban): make the comment action an inset icon button in the field
The labelled "Comment"/"Send" button floating inside a one-row compact
textarea read as part of the input and felt cramped. The field now
matches the new-project idea field, the closest analogue (a dialog
textarea with an inset icon action):

- default control padding, the Textarea's standard min height, and 13px
  text to match the rendered comments;
- a ghost arrow-up icon button (the chat composer's send glyph) inset
  `top-1 right-1`, labelled by aria-label and a Tip ("Comment", or "Send"
  while the task runs);
- 16px between the last comment and the field, instead of the list's
  12px rhythm.

The running task's requeue row below is unchanged.
2026-09-23 03:51:26 -07:00
Calvin Ng
815ecc378a style(kanban): even out the task modal's type scale and spacing
- The main column inherits the dialog's conversation text size instead of
  a stray text-sm, and its sections breathe on gap-5 so the description no
  longer butts against the feed's tab strip.
- Comments sit on gap-3 now that each body is a rendered markdown block.
- The off-scale 0.71rem (diagnostics detail, ready-unassigned callout, run
  rows) snaps to the 0.6875rem caption step used everywhere else here.
2026-09-23 03:51:26 -07:00
Calvin Ng
63f5bc0999 feat(kanban): render task text as markdown and drop the duplicated feed label
- Description, result, latest summary and comment bodies go through the
  app's MessageTextContent renderer instead of a pre-wrapped <p>, so
  agent-written `**Goal:**`, lists and inline code render as they do in
  chat. `media={false}`: kanban text is not session-scoped, so `MEDIA:`
  paths must not resolve against the active gateway.
- When the feed has more than comments, the segmented control is the
  heading (help tip on the same row); the Section label above it only
  repeated the active tab ("Comments · 2" twice). A comments-only feed
  keeps its label.
- Comment rows put author and time on one line above the rendered body.
2026-09-23 03:51:26 -07:00
Calvin Ng
50d8d22370 fix(kanban): tidy the task modal's sidebar property list
- Property rows are Sections, so every sidebar label (including Estimate and
  Attachments) uses FIELD_LABEL and one gap rhythm instead of MetaRow's
  own 0.65rem label style.
- Values wrap anywhere: the workspace path wrapped nowhere and was
  hard-clipped at the modal edge. The raw `dir: ` prefix is gone; a
  non-dir kind (scratch, worktree) is a muted badge, the path is mono and
  has a copy button.
- Priority uses the board card's amber up-arrow glyph, extracted to a shared
  PriorityGlyph so the two stay identical.
- The sidebar no longer starts ~1rem below the main column (py-4 vs no top
  padding); both columns share the header's baseline.
- Truncated dependency chips reveal the full linked-task title in a Tip.
2026-09-23 03:51:26 -07:00
Calvin Ng
eb3116ccba refactor(kanban): build the task modal on the shared Dialog primitive
The task modal was a hand-rolled fixed overlay: off-token chrome
(--ui-stroke-tertiary border, --ui-bg-elevated fill, bg-black/45 backdrop),
no focus trap, a window-level Esc listener, and dropdowns that portalled to
body underneath the backdrop, which needed a z-(--z-modal-popover) rung on
each menu.

DialogContent owns the shadow-nous / --stroke-nous chrome, the blurred
overlay, focus trap, Esc and outside-click dismissal, and publishes itself as
the portal container, so the status, assignee, actions and model menus open
inside the dialog and the per-menu z-index workaround goes away. The title is
a DialogTitle (the dialog's accessible name), the header actions use Button
icon-xs ghost, and the body sizes to its content up to the old cap instead of
a fixed height that left dead space under short tasks.
2026-09-23 03:51:26 -07:00
Ben Barclay
f596b312ce fix(kanban): dependency chips use aria-label, not native title=
The repo's no-native-title lint test forbids title= on buttons (use <Tip>
or aria-label). The chip's visible label already IS the linked task's
title, so the tooltip was redundant; aria-label carries it for a11y.
2026-09-23 03:51:26 -07:00
Ben Barclay
5104cb228e feat(kanban): design pass — dependencies to sidebar, Jira-style feed tabs
- Blocked by / Blocks move from the main column into the right property
  sidebar (chips resolve titles via link_tasks, short-id fallback kept).
- Main column's Comments/Activity/Runs/Worker-log sections collapse into
  one tabbed feed with a segmented control (Jira's 'Show: Comments |
  History | Work log'), defaulting to Comments; tabs with no content are
  omitted, and the control hides entirely when only comments exist.
  The comment composer (live-steer + note-and-requeue) rides the
  Comments tab.
2026-09-23 03:51:26 -07:00
Ben Barclay
2476c82837 feat(kanban): open tickets in a centered two-column modal; dependency chips resolve titles
- Desktop drawer -> Linear-style modal (smaller than Settings): main column
  holds diagnostics, description, result/summary, dependencies, comments,
  activity, runs, and the worker log tail; a right property sidebar holds the
  inline editors (assignee, model override) plus priority/tenant/workspace/
  created rows, estimate, and attachments. Backdrop click or Esc closes.
- GET /tasks/:id gains 'link_tasks' ({id,title,status} per linked task) so
  Blocks/Blocked By chips render titles instead of raw ids; older backends
  fall back to short ids. Additive; 'links' shape unchanged.
- New backend tests (test_kanban_link_tasks.py) + drawer tests for title
  chips and the id fallback.
2026-09-23 03:51:26 -07:00
Octavio Magaia
1750cfb066 fix(desktop): preserve local registry model profile scope 2026-09-23 03:48:15 -07:00
hermes-seaeye[bot]
6a0c3c2bc5 fmt(js): npm run fix on merge (#120113)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-23 10:45:07 +00:00
yoniebans
2e1afdf0ec fix(desktop): agent hand-overs of HTML open rendered; pane reads the tab's mode directly
Review follow-ups. An agent preview of an HTML file whose tab the user had switched to Source stayed in Source, so the agent could believe the page was on screen; the preview-tool route and the status-stack row now pass an explicit rendered mode (renderedHtmlTarget), while Files-pane re-opens still keep the user's pick. The pane read the store tab a second time and kept a test-only local mode; it now reads target.renderMode and offers the toggle only on a tab-backed pane. PreviewRenderMode names the mode once; canRenderHtmlFile is the reduced form; a stale load error or annotate draft no longer flashes for a frame on the switch to Source.
2026-09-23 12:39:13 +02:00
yoniebans
1adad98d10 fix(desktop): keep an HTML tab's Source pick on re-open and on a dirty file
Opening an already-open HTML file from Files re-normalised the tab to preview, so a Source pick was undone and the page ran again; openPreview now keeps the mode the tab is in unless the caller names one. Picking Source on a file with uncommitted changes landed on Diff, because LocalFilePreview mounted with no user mode and its auto mode is diff-first; the pane hand-off now counts as the pick.
2026-09-23 12:39:13 +02:00
yoniebans
9b68399138 refactor(desktop): drop openPreview's unused source argument
Since HTML files open rendered from every source, the PreviewRecordSource argument no longer changed anything; every caller still passed one. Remove the argument, the type and PreviewAttachment's source prop.
2026-09-23 12:39:13 +02:00
yoniebans
b5c4dbc0c3 fix(desktop): put the HTML source-mode switcher on the file header row
Follow-up to the Render | Source toggle for Files-pane HTML: in source mode the pane rendered its own switcher bar above LocalFilePreview's header, so the file showed two stacked header rows where Markdown shows one. LocalFilePreview now takes `onSelectRendered`; when present it adds `rendered` to its own switcher (next to Edit, as for Markdown) and routes the selection back to the pane. The pane-level switcher renders only in rendered mode, above the browser bar.

Also gate the toggle on a renderable target: a remote HTML file whose data URL failed validation arrives as `{ renderMode: 'source', transient: true }` with no `dataUrl`, and offering PREVIEW there would load a `file://` URL of a remote path.

Test asserts PREVIEW and Edit share one header row in source mode, and that the transient source fallback offers no PREVIEW. Prettier on the two lines the base commit left unformatted.
2026-09-23 12:39:13 +02:00
KoNit-K
c755d047c4 feat(desktop): render Files-pane HTML with a Render | Source toggle
Opening a local HTML file from Files now defaults to the existing
sandboxed preview path, with Source one click away on the same tab.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-23 12:39:13 +02:00
teknium1
7ed6534c7e Merge origin/main: browser fence composed with the dispatch/retry split (#115184); server registration, i18n, contracts 2026-09-23 03:25:06 -07:00
hermes-seaeye[bot]
9329575b4e fmt(js): npm run fix on merge (#120098)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-23 10:22:16 +00:00
teknium1
437e7456f5 feat(desktop): raise a bot's Screen tab when it starts using its desktop (opt-in)
Watching the bot drive its screen is the point of Bot Screen, but until now
you learned it had happened only afterwards. With "Open Screen when the bot
uses it" checked on a bot's row menu, the first live tool.start for a screen
tool (computer_use, browser_*) on a session the bot owns brings its Screen
tab forward.

Why opt-in and fenced: Desktop's rule is offer, don't hijack. The raise is
per bot (BotMeta.screenAutoOpen, rides profile ui_meta like pin/hide), never
moves keyboard focus (openBotScreen reveals the pane; the viewer grabs keys
only on Take over), never fires for replayed history (the reconnect replay
re-dispatches parked frames, so the wake is rate-limited to one per bot per
30 s rather than trusting seq), does nothing while the tab is open, and a
manual Close mid-run holds until the run has been quiet for a cooldown.

Live (isolated headless Electron + real serve backend, CDP): opt-in off →
tool.start opens nothing; toggled via the real menu → toast + aria-checked
true → tool.start opens "Hermes · Screen" (Screen is off state), focus stays
on the row; second call is a no-op; real Close → held at +2 s and +29 s,
raised again at +31 s.

Rule borrowed from thomasbek3/hermes-bot-kit computer-viewer's auto-connect.
2026-09-23 03:18:09 -07:00
teknium1
4b17b284ce test: keep cross-cutting architecture lints; drop stale OS-fake baseline entries
Restore the cheap repo-wide guards the per-file triage classed as source reads
but that protect recurring bug classes (<2s total):
- subprocess env scrubbing near spawn sites (credential leakage)
- gateway UTF-8 encoding= on file I/O (Windows mojibake)
- no raw yaml.safe_load of config.yaml (lost ${ENV} expansion)
- CLI subprocess.run timeouts (hung CLI)
- no locked readers on the shared state.db connection (#99349 segfault)
- CI classifier outputs / live-comment watch list match real workflows
- relay imports no platform crypto (relay trust boundary)
- Desktop relay deliver budget mirrors the Python deadlines (#93911)
- no native title= on Desktop buttons (DESIGN.md rule)

Drop _BASELINE entries in check_os_marker_fakes.py for files that no longer
fake macOS (the checker fails on stale entries), and remove doc/comment
pointers to deleted tests.
2026-09-23 03:15:26 -07:00
teknium1
38137d2b6e test: purge low-value tests, lane js06 (375 removed)
Change-detectors, tautologies, source-reading tests, redundant duplicates,
mock-echo tests and dead/unrunnable tests. Per-test rationale in the lane
ledger (category + reason for every removal).
2026-09-23 03:15:26 -07:00
teknium1
51766a72a3 test: purge low-value tests, lane js05 (169 removed)
Change-detectors, tautologies, source-reading tests, redundant duplicates,
mock-echo tests and dead/unrunnable tests. Per-test rationale in the lane
ledger (category + reason for every removal).
2026-09-23 03:15:26 -07:00
teknium1
93f06eda5b test: purge low-value tests, lane js04 (249 removed)
Change-detectors, tautologies, source-reading tests, redundant duplicates,
mock-echo tests and dead/unrunnable tests. Per-test rationale in the lane
ledger (category + reason for every removal).
2026-09-23 03:15:26 -07:00
teknium1
31d863c4ce test: purge low-value tests, lane js03 (206 removed)
Change-detectors, tautologies, source-reading tests, redundant duplicates,
mock-echo tests and dead/unrunnable tests. Per-test rationale in the lane
ledger (category + reason for every removal).
2026-09-23 03:15:26 -07:00
teknium1
0e8486d3c1 test: purge low-value tests, lane js02 (156 removed)
Change-detectors, tautologies, source-reading tests, redundant duplicates,
mock-echo tests and dead/unrunnable tests. Per-test rationale in the lane
ledger (category + reason for every removal).
2026-09-23 03:15:26 -07:00
teknium1
a4f93ce250 test: purge low-value tests, lane js01 (202 removed)
Change-detectors, tautologies, source-reading tests, redundant duplicates,
mock-echo tests and dead/unrunnable tests. Per-test rationale in the lane
ledger (category + reason for every removal).
2026-09-23 03:15:26 -07:00
teknium1
54c55307e3 test(desktop-e2e): never run a local e2e against the developer's real Hermes
buildAppEnv sandboxed HERMES_HOME only. Desktop now attaches to the host's
running hermes serve (one backend per host) and profile roots are
HOME-anchored, so on a machine running Hermes a local spec either attached
to that backend or listed the real profiles, and its sends went through
the developer's real model and state.db instead of the mock provider.

Set HERMES_DESKTOP_ISOLATED_BACKEND=1 (the documented private-backend
escape hatch) and HOME=<sandbox root>. CI has neither a running backend
nor real profiles, so it never took this path.
2026-09-23 02:28:38 -07:00
teknium1
680d613a01 fix(desktop): live-reload only loopback dev pages, one loopback predicate
Follow-up to the salvaged fix. Narrow the workspace live-reload gate to
loopback (full 127/8, ::1, 0.0.0.0, localhost) and drop `.local`: mDNS
names are other devices on the LAN (homeassistant.local), the same
"unrelated page loses its state on every agent file edit" class the fix
closes. The pane's error copy already had its own loopback regex for the
remote-gateway hint; both call sites now share isLoopbackPreviewUrl.

Tests trimmed to one pane invariant (reloads a loopback page, stops once
the tab browses elsewhere: the live page decides, not the tab's original
address) plus the predicate table.
2026-09-23 02:28:17 -07:00
fangliquanflq
8206d89b98 fix(desktop): preserve external preview page state 2026-09-23 02:28:17 -07:00
hermes-seaeye[bot]
a27b13056e fmt(js): npm run fix on merge (#120048)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-23 09:02:52 +00:00
teknium1
71b61884a4 fix(desktop): deliver a gateway event once even when two sockets to one backend carry it
The backend stamps `seq` once per event before its transport fan-out, so the
same frame arriving on two of the renderer's sockets to one process carries
the same (session_id, seq); `JsonRpcGateway` is per socket and cannot see the
other copy. `JsonRpcGateway.dispatchEvent` now tags each event with the
`replay_epoch` its socket adopted from `gateway.ready` (per process, not per
socket), and both fan-ins in `use-gateway-boot.ts` (primary + registry
secondaries) pass through one `createGatewayEventDedupe()` gate keyed by
(epoch, session_id, seq) before any store runs.

A duplicate is the same key within 30 s. Not "seq not above the highest
seen": the backend restarts a session's counter at 1 when the session leaves
its 64-session replay ring, and a high-water mark would swallow the whole
restart of any short session. Seq-less / session-less events always pass.
Bounded: 2048 seqs per session, 256 sessions LRU.

Live, with the router fix reverted so two sockets still join the chat: the
DOM never doubled a word (0/38 samples vs 4/40 on main) and the doubled
"reply was cut short" card is back to one.

Closes #120007. Part of #120005.
2026-09-23 01:56:12 -07:00
teknium1
0bb539b472 fix(desktop): session calls for a profile on the shared host backend ride the primary socket
Since #118246 one local `hermes serve` serves every profile, and main marks
those routes `sharedPrimary: true`. `requestGatewayForProfile` honoured the
flag, but the session-owner family (`requestGatewayForAgent`, retain/open/
ensure) went through `isAttachedSharedRemote`, whose `primaryConnectionMode
=== 'local'` early return (#113956, written while every local profile had
its own pooled child) sent every non-default local profile to a registry
secondary: a SECOND WebSocket to the SAME backend process. The backend joins
any socket that sends a session call to the chat's transport fan-out, so the
renderer received every event twice — "HelloHello from from the the mock
mock…" while streaming, duplicate interim bubbles, doubled error cards
(#120005, #119131, #119566, #119540, #118934).

The predicate is now `ridesPrimaryBackend`: it asks main for the route on
every call and reuses the primary socket (with the `profile` param) when the
descriptor carries `sharedRemote` (#96493) OR `sharedPrimary` (#118246).
#101416 stays fixed: when the probe fails on a LOCAL primary we never fall
back to the primary (a pooled profile's chat must not be minted under the
primary's pid), and a pooled descriptor (HERMES_DESKTOP_ISOLATED_BACKEND=1)
still opens its own secondary.

Closes #120006. Part of #120005.
2026-09-23 01:56:12 -07:00
teknium1
4a2bd7406e fix(desktop): models added by a plugin or catalog update no longer start hidden in the picker
The Edit Models store persisted only an allowlist of visible provider::model keys.
Once a provider had any stored key it was skipped by the default expansion, so a
model that appeared later (plugin update shipping a new route, catalog refresh,
new release) was absent from the list and rendered switched off. The store could
not tell "hidden on purpose" from "never seen".

Persist a `known` snapshot (hermes.desktop.known-models) beside the allowlist,
recorded whenever the user persists a choice. A curated provider now admits
models absent from the snapshot through the same curated default rule
(featured list / top-N); a provider the user hid outright stays hidden, new
models included. Stores written before the snapshot existed adopt the catalog
as judged the first time it loads, so existing hide choices are honoured
verbatim and only later arrivals count as new.
2026-09-23 01:35:20 -07:00
alt-glitch
5f47c35d37 fix: a connector row says "Waiting for your browser…" only after the user opens its link
The connect-first handoff (D85) has the build chat call manage_connections before
anything else, and the backend watcher mints each app's link on its first pass. That
moves the row to `initiated`, which the card drew as the spinner plus "Waiting for
your browser…" although nobody had clicked Connect and no browser had opened. Try
again had the same gap: the fresh link is minted, not opened.

The card now remembers which rows the user opened from it. A minted link the user has
not opened shows as not connected with the Connect button; clicking Connect opens it
and shows the waiting cue; Try again clears it until the new link is opened.
2026-09-23 11:54:42 +05:30
alt-glitch
e458619f23 fix: the first build is named and seeded from the welcome chat's stored reply, not its streamed copy
On Windows (run 3, 2026-09-23) the renderer's streamed copy of the setup
reply repeated its own chunks. The handoff card read its task and brief from
that copy, so the build session was titled "Set up mySet up my games an…" and
opened with "Set up Sid up Sid's PC's PC for gaming for gaming…", cut at 240
chars. The backend's stored reply held the clean directive.

The card now reads the handoff directive from session.history (the persisted
reply) and uses those values; an unreachable history falls back to the
rendered attrs. It also waits for the whole reply to stop running: `locked`
tracks only the text part, which a later part settles mid-reply.
2026-09-23 11:46:41 +05:30
hermes-seaeye[bot]
c0d7294769 fmt(js): npm run fix on merge (#119817)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-23 04:02:02 +00:00
alt-glitch
6b9214b08e fix: the build session's runbook names each installed plugin's skill by its loadable name
Live run: the first default turn called skill_view("blender") and got
"Failed to load skill: blender". A plugin skill registers under its
qualified name (`agent-plugin-<key>:<skill>`), and the runbook carried only
the catalog name, so the model guessed the short one. The install card's row
already reports the qualified name; it now rides the settled outcome into
the runbook, which tells the build agent to load it by that exact name.
2026-09-23 09:26:07 +05:30
alt-glitch
d41f079e4e feat: Blender and NVIDIA each get a recommended first task
Sid's ruling after walking the flow: both plugin families carry a pill.
"Help me make something in Blender" is offered everywhere (the catalog runs
Blender on all three platforms). "Set up my games and streaming" (NVIDIA App
+ Broadcast) is offered on a Windows PC with an NVIDIA GPU, where it leads,
since those plugins are Windows-only. Each task runs the install beat for
its own plugins.
2026-09-23 09:26:07 +05:30
alt-glitch
d06ada6b0b fix: the connectors card paints its rows at once and keeps them across remounts
Live run: the card showed no chips for 3 to 5 seconds, drew them, lost them,
and drew them again. connectors.list takes 6.7 to 8 s on this Mac, and the
catalog read another 6.7 s cold. The hook read once per MOUNT, and the card
remounts on every transcript rebuild (each hidden submit, each turn end), so
each remount threw the rows away and waited out another round trip.

Both reads now live in the query cache keyed by the guide session, and the
kickoff prefetches them when the guide session opens (created or adopted),
two turns before the card needs them. A remount paints the cached rows.
2026-09-23 09:26:07 +05:30
alt-glitch
6ec517c475 feat: onboarding offers catalog plugins beside connectors and installs the picked ones before handoff
NS-960. One card, one group ("connectors"): the curated catalog plugins
this OS runs lead the hosted connectors (D1, D4). A plugin whose app is
absent is greyed with the reason and stays pickable (D5). Picks land in
answers.plugins and ride the existing [setup] note to the guide.

The guide's runbook gains the install beat as the last thing before the
handoff card (D2, D3): narrow the picks to what the chosen task needs, then
ONE manage_catalog install call. A new suggested first task sits beside the
existing ones and installs its plugins through that same beat: "Set up my
games and streaming" (NVIDIA App + Broadcast) on a Windows PC with an NVIDIA
GPU, "Help me make something in Blender" everywhere else.

When the guide's install card settles, each plugin row's outcome
(installed / failed / skipped, whatever the user did) is written into the
answers (D6). The build session's runbook names what is ready, what was
offered and not installed, and what was picked but not offered, and tells
the build agent never to install. profiles.remember_onboarding records the
picked plugin names in the default profile's memory, next to the connectors.
2026-09-23 09:26:07 +05:30
alt-glitch
5c215ffa21 feat: catalog marks onboarding plugins; catalog rows carry the app's presence
The onboarding card needs to list catalog plugins beside the hosted
connectors (NS-960 D1, D4) and grey a plugin whose app is absent (D5).
The catalog had no curated flag, and the manage_catalog row left
app_state empty.

- `onboarding: true` and `title` on catalog entries (loader, validator,
  docs); set on blender, nvidia-app and nvidia-broadcast.
- hermes_cli/plugin_catalog_presence.py reads the plugin.json at the
  catalog's pinned commit once per pin and judges its app declaration with
  the hermes_platform resolver the installer and the Plugins-tab pill use.
  No declaration or an unreadable one is `unknown`, never `present`.
- `plugins.manage action=onboarding` lists the curated entries this OS
  runs (platform mismatch is the only exclusion) with app_state and the
  sentence the card greys the row with.
- manage_catalog plugin rows now carry app_state and the catalog title.
- A live entry that differs from the in-tree entry at the same pin (new
  metadata) now follows the same newer-catalog rule as a new pin, so a
  checkout that adds `onboarding` is not masked by a published doc that
  predates it.
2026-09-23 09:26:07 +05:30
hermes-seaeye[bot]
731e99d15d fmt(js): npm run fix on merge (#119739)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-23 02:21:22 +00:00
brooklyn!
40f29e280c test(desktop): wait for the endpoint form before editing it 2026-09-22 21:15:22 -05:00
brooklyn!
7eb0ae1a60 fix(desktop): keep multi-gateway navigation available in Simple mode 2026-09-22 21:15:22 -05:00
brooklyn!
9867a75c1e fix(desktop): load saved gateways independently of optional chrome 2026-09-22 21:15:22 -05:00
hermes-seaeye[bot]
d3b25b52ad fmt(js): npm run fix on merge (#119649)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-22 23:49:00 +00:00