Commit Graph

5742 Commits

Author SHA1 Message Date
teknium1
36f165a162 test: run the native HUD gesture C contract on the Linux JS lane
#120071 deleted hud-modifier-native.test.ts (darwin-only, so it ran nowhere:
js-tests is ubuntu-only) and its native fixtures. hud-modifier-gesture.h is
the clean-tap state machine both the macOS and Linux XI2 helpers include and
is portable C, so the restored test compiles hud-modifier-gesture.test.c with
cc (xcrun clang on macOS) and runs it inside the electron vitest project,
which js-tests runs via check:test:desktop:platforms. Loosening the 500 ms
tap bound in the header turns it red.

The macOS event-adapter (.m), Windows C# (.cs) and node:test (.mjs) files are
not restored: they need a macOS/Windows toolchain and no JS lane runs there.
2026-09-23 10:34:54 -07:00
teknium1
dae17c20db test: restore Desktop composer-core and inline-code theming guards dropped by #120071
composer-text-guard.test.tsx is rebuilt on the REAL useComposerDraft hook (the
deleted file tested an in-test copy of the helper): the mount-time draft
restore must not throw while assistant-ui's composer core is unbound, and must
write through once bound (#49903 / #49488). Removing the hook's try/catch
turns it red.

The two inline-code tests render the real component with the real
styles.css injected and read the computed style of the rendered <code>; they
are cascade outcomes, not source greps. Re-scoping the inline-code rule to the
assistant slot (the original bug) turns both red.
- system-message.test.tsx: background report inline code uses the chat
  inline-code tokens (#107486)
- group-chat-view.inline-code.test.tsx: room message inline code uses the chat
  inline-code tokens (#114086)
2026-09-23 10:34:54 -07:00
teknium1
52d4ba9ed8 test: restore Desktop gateway spawn-priority forwarding guards dropped by #120071
Each entry point forwards spawnPriority through its own branch to the
Electron bridge (getConnection / getConnectionFor); nothing else asserts the
IPC payload for these doors. Sabotage check: dropping spawnPriority from
retainGatewayForAgent's plain-profile branch turns the null-connection test red.

Restored (apps/desktop/src/store/gateway-spawn-priority.test.ts):
- openGatewayForProfile without a priority never tags a dial as foreground
  (pre-warms stay background slots, #102281)
- openGatewayForAgent forwards spawnPriority to every registry dial (#102281)
- retainGatewayForAgent tags the lease dial when the caller says foreground (#105104)
- retainGatewayForAgent without options never tags a registry dial (#105104)
- retainGatewayForAgent on the plain-profile route (null connection) still
  dials foreground (#110354 branch, gatewayForProfile path)
2026-09-23 10:34:54 -07:00
teknium1
f4b9694da4 test: restore regression coverage #120071 dropped as dead or redundant
Review on #120071 (yoniebans) found tests deleted as 'dead' that were only
mis-gated, and regression tests with no remaining equivalent:

- Discord voice receive (real NaCl/RTP: wrong-key drop, DAVE decrypt
  failure, malformed padding, non-allowlisted speaker) moves out of the
  never-selected tests/integration/ to tests/plugins/platforms/, where no
  conftest stubs discord.py; 34 pass with the messaging extra.
- Five *_windows_live.py files had a bare skipif(win32), so
  list_os_marked_tests.py never selected them; now windows_only.
- iron-proxy token-swap E2E (opt-in gate) is back.
- Gateway regressions: cached agent iteration cap (#48127), clarify JSON
  never renders as progress (#52374), handoff watcher DB work off the event
  loop, delivery ledger single connection, checkpoint prune and memory trim
  housekeeping ticks.
- Desktop: command-screenshot IPC rejection, inactive WSL bridge never
  spawns wsl.exe, bootstrap runner git binary.
2026-09-23 10:34:54 -07:00
teknium1
62ac203abe fix(desktop): let a deleted bot section's shield expire when its member clear finishes
Review follow-up. The delete shield on adoptBotSectionsFromMeta was a
session-long Set: once a section was deleted here, a member filed back into
that id later (by another desktop that still has the section) could never
make it reappear on this one, and the module-level Set leaked between tests.

- The shield now lasts only while the delete's member clear is running and
  expires when moveBotsToSection(members, null) settles. After that no member
  cleared here carries the id, so one that does was filed there again and
  adopting it is right.
- A per-delete token keeps an earlier delete's clear (delete -> Undo ->
  delete) from lifting the shield while the later delete is still clearing.
- resetBotSectionDeletes() test seam, called from the user-sections beforeEach.

One invariant test: red on the PR head (shield never expires) and red with
the token guard removed (first clear lifts it early); green with this fix.
2026-09-23 10:25:37 -07:00
teknium1
3b36436e4c fix(desktop): deleting a bot roster section no longer brings it back under its old name
A delete clears its members one profile write at a time. While a write is
slow (a remote gateway, a pooled local backend waiting for a slot), the
not-yet-cleared members still carry the section's id and name in ui_meta,
and adoptBotSectionsFromMeta (#114983) rebuilt the section the user had just
deleted - under whatever name the member still carried, so a renamed section
came back with its pre-rename name.

- adoptBotSectionsFromMeta skips sections deleted on this desktop this
  session; Undo takes the id back out.
- moveBotsToSection stops when its target section is deleted mid-loop, so a
  rename re-stamp stuck behind a slow write cannot refile members into the
  deleted section afterwards.
2026-09-23 10:25:37 -07:00
teknium1
17ad4d7465 fix(bot-mode): a local group member is never re-driven on its own reply
Since 10a78f9916 every member that knows its connection stamps its reply
with `from.source` (local members too, e.g. "This device"). The round's
own-entry watermark walk (e00aa13d7d, written against the pre-stamp base)
still only expected a source for remoteSource members, so a local member's
sourced reply did not read as its own: the watermark stayed before it, the
member got its own reply back as "new messages in the room" and answered
itself again, turn after turn, until the round cap. Live, a room that should
settle after one reply kept the Stop button up and posted extra replies.

authoredByMember now uses the same authorship rule as the `(you)` suffix
(isGroupChatSelf: gateway install_id first, then connection label/id, an
unsourced line only for a local viewer), so the two can no longer drift.
2026-09-23 10:24:34 -07:00
brooklyn!
8230b524a5 test(desktop): cover localized slash help and cached locale switches 2026-09-23 12:07:22 -05:00
brooklyn!
2d585eb0b2 fix(desktop): localize live slash-command descriptions
Adapt the shared runtime resolver, canonical descriptions, preserved backend usage text, and locale-sensitive completion invalidation. Keep custom skill metadata and command identifiers unchanged.

Co-authored-by: leo yang <2717736005@qq.com>

Co-authored-by: T910317 <107766948+T910317@users.noreply.github.com>
2026-09-23 12:07:22 -05:00
hermes-seaeye[bot]
904c110ef8 fmt(js): npm run fix on merge (#120472)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-23 17:05:26 +00:00
brooklyn!
7674328745 test(desktop): preserve profile selection through compact scope labels 2026-09-23 11:58:44 -05:00
brooklyn!
4f9f1f96f6 fix(desktop): clarify plugin scope copy and contain its selector
Adapt the focused header fixes to the current scope selector and complete all six locale catalogs.

Co-authored-by: kokhlo <konstantin.khlopkov93@gmail.com>

Co-authored-by: KoNit-K <konit.block@protonmail.com>
2026-09-23 11:58:44 -05:00
hermes-seaeye[bot]
55138d85b2 fmt(js): npm run fix on merge (#120465)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-23 16:48:05 +00:00
hermes-seaeye[bot]
c9dca72651 fmt(js): npm run fix on merge (#120459)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-23 16:41:23 +00:00
brooklyn!
bb4130f6f5 test(desktop): cover Unicode profile initials and default glyph behavior 2026-09-23 11:14:55 -05:00
brooklyn!
2b18ca7867 fix(desktop): preserve Unicode graphemes in profile initials
Use one shared grapheme-safe initial without widening the existing rail or changing profile identity and color behavior. Adapt the single-initial direction from the competing proposals.

Co-authored-by: Moisés Valero <moisesvs84@gmail.com>
2026-09-23 11:14:55 -05:00
brooklyn!
3f06838e04 test(desktop): keep archive instructions aligned with row gestures 2026-09-23 11:14:28 -05:00
mrchen0709
afea7ff5fc fix(desktop): teach the real archive gesture in the archived-sessions copy
The Archived sessions intro told users to Ctrl/⌘-click a chat in the sidebar to
archive it. That gesture opens the chat in a new tab: resolveSessionRowClick maps
⌘/⌃-click to 'newTab' and only ⌥+⇧-click to 'archive'. Anyone following the copy
never archives anything, and that line is the only in-app hint the gesture exists.

Swap the modifier in the six locales carrying the string, keeping each
translation's wording. The resolver and the row's own comment already document
⌥+⇧ — only the user-facing copy was stale.

Fixes #107999
2026-09-23 11:14:28 -05:00
brooklyn!
af63503a0f test(desktop): preserve changelog semantics across locale labels 2026-09-23 11:11:29 -05:00
brooklyn!
c312d39931 fix(desktop): localize update groups and fallback release copy
Adapt David Metcalfe’s proposal to the current update overlay, preserving commit identity, ordering, counts, and the pure formatter’s English defaults.

Co-authored-by: David Metcalfe <80915+DavidMetcalfe@users.noreply.github.com>
2026-09-23 11:11:29 -05:00
hermes-seaeye[bot]
6cd727bbfa fmt(js): npm run fix on merge (#120400)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-23 15:53:24 +00:00
hermes-seaeye[bot]
8191aa0660 fmt(js): npm run fix on merge (#120392)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-23 15:46:42 +00:00
doresa0
03544a73be fix(desktop): keep reasoning settings label on one line
Prevent short CJK labels like 推理 from wrapping awkwardly next to the
effort select in Model settings.

Closes #83118
2026-09-23 10:38:40 -05:00
Austin Pickett
bd945ec384 fix(state): surface corrupt state.db as one degraded session-storage state (#120274)
* fix(state): publish structural state.db corruption as one profile-level state

A structurally corrupt state.db showed up differently on every surface: the
sidebar endpoint returned 200 with empty slices plus an errors row, /api/sessions
returned 500, /api/status said components.storage ok and readiness was green.
None of them said the store was damaged, so Desktop rendered it as deleted
history (#72046).

hermes_state_health is now the single latch, keyed by resolved state.db path:

- SessionDB._halt_db_corrupt, the SessionDB read helpers, the web profile
  reader and the readiness probe publish into it, only for structural
  corruption (not FTS-scoped damage, not the malformed-schema case the web
  open path heals).
- gateway.readiness reports it (state_db degraded/corrupt, and session_store
  unavailable/corrupt even when the handle cache says ok), which also feeds
  /api/status components.storage (now with reason: corrupt).
- /api/sessions, /api/profiles/sessions and /api/profiles/sessions/sidebar
  carry storage: {profile: "corrupt"}; /api/sessions returns 503
  state_db_corrupt instead of 500.
- A peer SessionDB handle in the same process refuses writes on a latched
  path with the existing StateDbCorruptError, so gateway/agent transcript
  diversion and classify_persistence_error keep working unchanged.

The latch never clears on its own and resets on restart, the recovery boundary
StateDbCorruptError already documents.

Co-authored-by: konsisumer <konsisumer@users.noreply.github.com>

* fix(desktop): say the session store is damaged instead of an empty sidebar

The sidebar reads the list endpoints' new storage map into
$corruptSessionStores and renders a persistent destructive Alert above the
session list naming the affected profile(s). The copy says missing chats were
not deleted and points at the non-destructive path (quit Hermes, then
`hermes sessions recover --source <state.db> --inspect-only` or restore a
snapshot) plus the recovery guide; it does not recommend `sessions repair`
for structural damage.

Co-authored-by: konsisumer <konsisumer@users.noreply.github.com>

---------

Co-authored-by: konsisumer <konsisumer@users.noreply.github.com>
2026-09-23 10:17:46 -04:00
Xipong
5c6b56a866 fix(desktop): keep offset-drifted backfill rows in order (#119606)
Co-authored-by: Xipong <217837358+Xipong@users.noreply.github.com>
2026-09-23 14:12:13 +00:00
Austin Pickett
8fb0fc6ae6 fix(desktop): a submit that resumes the selected session moves the chat to the resumed runtime (#120273)
When submit cannot prove the pane's runtime owns the selected stored session
(reverse binding lost to eviction, reconnect, or a compression rotation the
selection never followed), it resumes the stored session and continues on
the runtime id that session.resume returns. That path pinned only
activeSessionIdRef. ChatView renders the $sessionStates slice named by
$activeSessionId, so the optimistic prompt, the reply, and every later turn
landed in a slice the pane never painted, while the legacy $messages mirror
(which the ref drives) looked correct. The chat stayed frozen until a relaunch.

Rebind the atom together with the ref, as the session-not-found recovery in
the same file already does, and carry the transcript the pane was showing
into the resumed runtime's empty slice (session.resume omits messages) when
both name the same conversation by lineage. A pane runtime that belongs to a
different stored session is never carried over.

Refs #71733
Refs #117867
2026-09-23 10:06:51 -04:00
liuhao1024
5c4db8d8b0 test(desktop): pin the failed-turn reseat after a truncating retry
Regression tests from #118719: a failed turn the re-submitted prompt truncated
out of the stored history returns to its timeline position, and a failed tail
whose retry is still local-only stays at the end.
2026-09-23 10:05:09 -04:00
Austin Pickett
865e94672e fix(desktop): keep preserved error turns in place and drop stored copies
preserveLocalAssistantErrors appended every kept local error run after the
refreshed transcript, so an older failed turn repainted below newer turns
(#118002). Reseat each run after the refreshed row that preceded it locally,
and drop a run the refresh already stored under new ids (same role/text
sequence in the gap).

Co-authored-by: liuhao1024 <sunsky.lau@gmail.com>
2026-09-23 10:05:09 -04:00
Austin Pickett
88e3c976ae fix(desktop): skip durably completed replies compaction re-inserted 2026-09-23 10:05:09 -04:00
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