Commit graph

1802 commits

Author SHA1 Message Date
Brooklyn Nicholson
dc50ddbbd6 style(desktop): hold the delegation card to three-quarter width
A list of short rows — goal, model, timer — reads better narrow than stretched across the full reading column. Scoped in styles.css rather than as a utility: the rule above it sets width on every tool block and wins over one.
2026-07-28 03:15:20 -05:00
Brooklyn Nicholson
2818c9e514 feat(desktop): render delegate_task as its live subagent list
A fan-out showed up in the transcript as one grey row and a JSON blob, so the several agents it started were invisible until they finished. Give the call its own card: one line per child with the goal, the model running it, and a live timer, over a single ticking line of that child's relayed activity.
2026-07-28 03:05:14 -05:00
hermes-seaeye[bot]
9a5f102d0e
fmt(js): npm run fix on merge (#73197)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-28 07:27:44 +00:00
brooklyn!
a9c9467dd8
Merge pull request #73178 from NousResearch/bb/dupe-message-id
fix(desktop): stop a duplicate message id from crashing the workspace pane
2026-07-28 02:19:12 -05:00
Brooklyn Nicholson
14790234ff fix(desktop): stop a duplicate message id from crashing the workspace pane
A repeated id in the transcript reached assistant-ui's MessageRepository,
which throws on the second link and takes the whole workspace pane down to
the contribution error boundary — the pane the user is actually working in.

Dedupe where the repository export is built, so no upstream transcript bug
can crash the pane, and close the journal merge path that produced one: a
resume that replays a still-journaled turn appended rows the base already
held by id.
2026-07-28 02:03:05 -05:00
brooklyn!
4da7b9ee02
Merge pull request #73169 from NousResearch/bb/queue-drain-semantics
fix(gateway): a queue drain never becomes a live-turn correction
2026-07-28 01:58:31 -05:00
brooklyn!
48b21acb90
Merge pull request #73164 from NousResearch/bb/pins-out-of-lists
fix(desktop): keep pinned sessions out of the unpinned sidebar lists
2026-07-28 01:54:08 -05:00
Brooklyn Nicholson
ab68c5efec fix(gateway): a queue drain never becomes a live-turn correction
The desktop's queue promises "run this AFTER the current turn", but the
promise broke on a race the user can't see: a drain that fired when the
client observed idle while the server was still unwinding the turn landed
in _handle_busy_submit, which applied busy_input_mode — redirecting or
interrupting the live turn with text the user explicitly queued. That's
why force-sending the queue felt like a dice roll: the same gesture
steered, interrupted, or queued depending on a millisecond settle race.

prompt.submit now carries queued:true on every fromQueue drain (composer
auto-drain, send-now, background drain), and the gateway's busy path
honors it by forcing queue semantics — never steer, never redirect,
never interrupt. Lose the race and the text simply waits its turn, which
is what queueing meant all along.

Covered on both sides: a gateway test proving a queued drain cannot touch
redirect/steer/interrupt on the live agent, and the desktop drain tests
now assert the flag rides every prompt.submit shape (direct, background,
resume-retry).
2026-07-28 01:48:08 -05:00
hermes-seaeye[bot]
f2a4452c8a
fmt(js): npm run fix on merge (#73165)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-28 06:47:12 +00:00
Brooklyn Nicholson
c3a3814885 fix(desktop): keep a worktree lane whose only sessions are pinned
Filtering pins out of the project tree was deleting the lane along with
its last row, so pinning your only chat on a branch made the worktree
vanish from the sidebar — and a project whose sessions were all pinned
fell through to the "no sessions" empty state.

A lane is structure: it exists on disk and you can still start work in
it, which is why the git-worktree enhancer injects lanes that never had
a session at all. Keep emptied lanes, count a lane (not a row) as
project content, and teach the live overlay's prune to drop only the
lanes IT emptied.
2026-07-28 01:45:59 -05:00
Brooklyn Nicholson
7806e6a9a8 fix(desktop): keep pinned sessions out of the unpinned sidebar lists
A pin belongs to the Pinned section and nowhere else, but only the flat
recents list filtered them — the project overview, an entered project's
lanes, and the messaging platform sections all still rendered a pinned
row a second time.

Filter every unpinned group on a shared predicate that matches the live
id AND the durable lineage-root pin id, so a compression tip rotation
can't leak the row back in. A platform's "load more" count discounts its
pinned rows too, or it promises rows that never appear.
2026-07-28 01:39:20 -05:00
brooklyn!
cef85482fc
Merge pull request #73158 from NousResearch/bb/tiny-model-switches
fix(desktop): shrink model-visibility switches to xs
2026-07-28 01:38:56 -05:00
brooklyn!
e95ce5a0e1
Merge pull request #73146 from NousResearch/bb/steer-transcript-scaffolding
fix(agent): keep interrupt-checkpoint scaffolding out of the steered transcript
2026-07-28 01:36:15 -05:00
Brooklyn Nicholson
69a4f65164 fix(desktop): use xs switch size in model-visibility dialog 2026-07-28 01:32:24 -05:00
hermes-seaeye[bot]
c80b199f36
fmt(js): npm run fix on merge (#73155)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-28 06:29:04 +00:00
brooklyn!
4289a934b2
Merge pull request #73147 from NousResearch/bb/session-open-in-place
desktop: open sessions where they already are
2026-07-28 01:20:53 -05:00
brooklyn!
befff02e9b
Merge pull request #73150 from NousResearch/bb/session-tab-slots
desktop: ⌘1–9 maps to visible tabs
2026-07-28 01:18:52 -05:00
Brooklyn Nicholson
b6c7df6cb6 fix(desktop): sort session-row imports for eslint 2026-07-28 01:13:30 -05:00
Brooklyn Nicholson
319bcedfea desktop: open sessions where they already are
Notifications, ⌘K, switcher, session picker, cron/command-center, artifacts,
and @session refs all go through openSession. Plain click/Enter focuses the
existing tile or main; ⌘-click/⌘-Enter opens a new tab; ⇧⌘ still pops a window.
2026-07-28 01:13:30 -05:00
Brooklyn Nicholson
e67e4604db desktop: shared openSession door for focus-or-route
Centralize open-in-place / tab / window so every surface can jump to an
already-open tile or main tab instead of forcing main.
2026-07-28 01:13:30 -05:00
Brooklyn Nicholson
f65fa01926 desktop: ⌘1–9 slots map to visible tabs, not raw pane indices
Focus layout keeps `files` in the workspace group's panes while chrome-
hidden. Slot keys walked that raw array, so ⌘2 landed on the first session
chip and every later number was off-by-one (especially after a ⌘W main-tab
shift). Index the same shown filter the strip paints, and cycle the same.
2026-07-28 01:12:43 -05:00
Brooklyn Nicholson
589fd23947 test(desktop): cover floating pane adoption and live drag
Adoption: a floating contribution stays out of the tree while a docked
sibling in the same pass is adopted, so the exclusion is specific rather
than a broken run.

Live DOM: mounts FloatingPanes and drives real pointer + resize events —
anchored spawn, drag, persistence across remount, edge-riding on resize,
the titlebar floor, collapse, and the no-drag button opt-out.
2026-07-28 01:11:12 -05:00
Brooklyn Nicholson
aad7f128bb feat(sdk): expose FloatingAnchor to plugins
Plugins previously had to fake a floating pane by registering into a
chrome slot and rendering position:fixed themselves, which broke inside
any transformed ancestor and reimplemented drag, clamping, and
persistence each time.
2026-07-28 01:11:12 -05:00
Brooklyn Nicholson
f78521785f feat(desktop): render floating panes above the layout tree
Adds placement: 'floating' — the one non-tiling pane role. Adoption skips
those contributions so one can never become a track that steals width
from a zone; the tree renders them as fixed cards instead, draggable by
the header, with position and collapse persisted per pane id.

The card reuses HUD_SURFACE (command palette, session switcher) rather
than hand-rolling a second float surface.
2026-07-28 01:11:05 -05:00
Brooklyn Nicholson
6dd1c6c062 feat(desktop): floating pane geometry
Pure clamping/anchoring rules for a pane that lives outside the layout
tree: keep 48px grabbable at either horizontal edge, hard-bound the top
below the titlebar so the drag handle stays reachable, pin top-left
rather than invert when the card outgrows the viewport, and let an
edge-anchored card ride its edge as the window resizes.

No DOM, so the rules are testable directly.
2026-07-28 01:10:59 -05:00
Brooklyn Nicholson
c883367bd2 fix(agent): keep interrupt-checkpoint scaffolding out of the steered transcript
A mid-stream steer persists an interrupted-turn checkpoint so the model knows
its reply was cut off. That scaffolding — "[This response was interrupted by a
user correction.]" and the "Visible response before the interruption:" header —
was written straight into message content, so every reload painted the raw
machinery as an assistant bubble (and merged it into the preceding tool-call
bubble). Steered transcripts became unreadable.

Reuse the existing display/replay split instead of inventing new surface:
- Carry the scaffolded form in the server-only api_content sidecar (the exact
  bytes replayed to the provider), keep content the user's/agent's real words.
- When nothing reached the screen there is no clean form, so mark the row
  display_kind=hidden — replayed to the model, dropped by every transcript
  surface, exactly like compaction-reference rows.
- Honor display_kind=hidden in the gateway's _history_to_messages projection
  (it only sniffed the [System: convention), so the checkpoint can't leak
  through the live/resume path to the TUI/CLI either.

The model still receives the full interrupted context on the wire; the
transcript shows the partial reply and the user's correction.
2026-07-28 01:05:02 -05:00
brooklyn!
5b22bd9556
Merge pull request #73105 from NousResearch/bb/tighter-turn-block-gap
Tighten the transcript's scaffolding rhythm, give diffs room
2026-07-27 23:52:49 -05:00
brooklyn!
4cee9aab61
Merge pull request #73075 from NousResearch/bb/drop-leva-backdrop-controls
refactor(desktop): drop leva from the chat backdrop
2026-07-27 23:48:10 -05:00
brooklyn!
0ae2997345
Merge pull request #73110 from NousResearch/bb/composer-at-chip
Chip @ file/folder refs instead of dropping them as plain text
2026-07-27 23:46:14 -05:00
Brooklyn Nicholson
16ef964fdb feat(desktop): rest the thinking caret at a faint hint instead of invisible
A run of thinking headers is the one place the disclosure affordance isn't
otherwise discoverable — every other one sits in a row you're already
reaching for. The resting opacity becomes a token so only that surface opts
in; DisclosureRow is shared with tool rows and run headers, which keep the
invisible-until-hover default.
2026-07-27 23:27:35 -05:00
Brooklyn Nicholson
2881243df3 fix(desktop): tighten the gap between adjacent transcript scaffolding
The turn block gap is the space between the reply and the work around it, so
a back-to-back run of thinking headers and tool rows spent the full gap
between every line and read as a stack of cards rather than one column.
Adjacent scaffolding now ticks at a third of it.

The hook is the block list the rhythm rule already enumerates, minus prose,
rather than `data-conversation-scaffold`: that marker is absent on a
multi-call tool group — most of a real run — and present on rows nested
inside one, so it both missed the lines that needed tightening and moved
ones that didn't.

An open file edit is the exception. A diff is the deliverable, read like a
PR, so it keeps the full block gap on both sides while the scaffolding
around it stays tight. A streaming turn seals blocks into separate bubbles
where the flex gap would restore the full gap, so that seal is corrected to
match — and skipped when either side carries a diff.
2026-07-27 23:27:35 -05:00
Brooklyn Nicholson
90797eb224 fix(desktop): chip a bare @path into the ref it means
Tab-descending into a folder from the @ popover re-types the token as a
bare `@apps/desktop/` so the next complete.path lists its children. That
is right while typing, but a bare token is not a reference —
REFERENCE_PATTERN only matches @kind:value — so a draft sent with one
rendered as plain text and attached nothing at all.

Promote it to @file:/@folder: on the way out, the same shape url-refs.ts
uses for bare links: on the committing space, on paste, and on submit for
a path that never got its space. A '/' is required so a handle like
@teknium1 is never mistaken for a path.
2026-07-27 23:13:32 -05:00
hermes-seaeye[bot]
3af7b867fd
fmt(js): npm run fix on merge (#73107)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-28 03:59:15 +00:00
Brooklyn Nicholson
73f8ddbb8b fix(desktop): stop hoisting an @ ref that is already inline in the prose
displayContentForMessage re-derived every ref from the attached context
block and re-emitted it as a detached list above the text. Now that the
token survives expansion, that list duplicated the inline chip. Hoist
only the refs the prose is missing, so turns persisted by an older
backend still render their chips.
2026-07-27 22:55:24 -05:00
brooklyn!
56aacbdb01
Merge pull request #73101 from NousResearch/bb/queue-double-send
feat(desktop): double-Enter sends the queued turn
2026-07-27 22:50:51 -05:00
Brooklyn Nicholson
ac8310bcc2 test(desktop): cover the busy empty-Enter double-send 2026-07-27 22:43:42 -05:00
hermes-seaeye[bot]
9f4a6fdf4a
fmt(js): npm run fix on merge (#73096)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-28 03:43:07 +00:00
Brooklyn Nicholson
84d77b608c feat(desktop): double-Enter sends the queued turn, and the row says Enter
Empty Enter while busy was a hard no-op, so a queued turn could only be
sent early via the panel's send arrow or Cmd+Shift+K. With prompts
queued, a second Enter now promotes the head and interrupts, mirroring
the idle empty-Enter drain. Nothing queued keeps the old no-op.

The panel's send-now row action switches from a bare up-arrow to the
return glyph, so the row states the keybind the double-send uses.
2026-07-27 22:42:21 -05:00
brooklyn!
ef26701134
Merge pull request #73089 from NousResearch/bb/desktop-sidebar-counts
Drop the remaining session counts from the sidebar
2026-07-27 22:33:09 -05:00
Brooklyn Nicholson
67ea00327d style(desktop): drop the remaining sidebar session counts
#72336 removed the count from the flat list and #72912 from the entered
project, but three sites still tallied rows: search results, each
messaging group (Telegram, Discord, …), and cron jobs.

With them gone SidebarCount never renders a count — its one caller is
the projects-loading spinner — so rename it SidebarSectionMeta.
2026-07-27 22:27:26 -05:00
Brooklyn Nicholson
058c4376d2 refactor(desktop): drop leva from the chat backdrop
The backdrop shipped a leva control panel — a Shift+Y dev tweak surface
for opacity, blend mode, filters and a radius scalar — that has never
been touched since the app landed. Its hooks ran in every build, dev and
packaged alike: only the panel's visibility was gated on
`import.meta.env.DEV`, not the two `useControls` calls or the zustand
store behind them. Inline the values it was already rendering with and
delete the dependency; leva was the only thing importing it in the tree.

One of those controls was doing real damage. `--radius-scalar` shipped as
0.6 in styles.css, but the slider's 0.2 default was written onto the root
element on mount, so every window that mounts the backdrop rendered at
0.2 and every window that doesn't kept 0.6. The token now declares 0.2 —
the value the chat has actually rendered at all along.

The remaining static values (0.025 opacity, difference blend, 160dvh, top
left, invert) become classes; saturate(1)/brightness(1) were identity and
are dropped.
2026-07-27 22:18:51 -05:00
brooklyn!
202140db53
Merge pull request #73074 from NousResearch/bb/slash-after-command
fix(desktop): keep slash completion alive after a leading command
2026-07-27 22:16:35 -05:00
brooklyn!
c1964f977b
Merge pull request #73047 from NousResearch/bb/link-brand-icons
feat(desktop): brand icons on links to known domains
2026-07-27 22:16:24 -05:00
brooklyn!
9ed212e096
Merge pull request #73062 from NousResearch/bb/sane-tooltips
fix(desktop): stop tip-wrapping kebab menu triggers
2026-07-27 22:16:10 -05:00
brooklyn!
ea0775eb8b
Merge pull request #73073 from NousResearch/bb/composer-image-lightbox
fix(desktop): open a composer image attachment in the lightbox, not the rail
2026-07-27 22:16:01 -05:00
Brooklyn Nicholson
015353d970 test(desktop): cover short actions labels and tip-less close buttons 2026-07-27 22:03:37 -05:00
Brooklyn Nicholson
970c4b5336 fix(desktop): shorten Actions aria-labels and tautological copy
Use short static labels (Session actions / Actions) instead of
Actions for <name>. Trim toast fallback, settings echoes, YOLO click
lectures, and fat gateway/appearance/notifications intros.
2026-07-27 22:03:37 -05:00
Brooklyn Nicholson
9b5c15727c fix(desktop): strip Close-X tips and statusbar tip lectures
Drop tips on dialog/overlay/find-bar/review/master-detail close buttons.
Stop tip-wrapping connection/gateway/timer/context/cron/webhook chrome
that already names itself on screen; keep tip only when there's a real
gateway reason. Drop the titlebar swap paraphrase.
2026-07-27 22:03:37 -05:00
Brooklyn Nicholson
5dadacfce7 docs(desktop): ban Close-X tips and click-to chrome lectures
Extend the tip rule past kebabs — no tip on dismiss X, and no tips that
only paraphrase a visible label or say "click to…".
2026-07-27 22:03:37 -05:00
Brooklyn Nicholson
eb851d619e fix(desktop): keep slash completion alive after a leading command
Typing a second slash command in the composer went dead whenever the
message started with one. `/work /cle` offered nothing, while `do /work
then /cle` completed fine — which read as an intermittent glitch rather
than a rule.

Two regexes detect a slash, and the `^`-anchored command shape was tried
first. Its argument tail (`(?:\s+\S*)*`) swallows the rest of the line,
so a later `/skill` parsed as an argument to the first command; a command
that takes no options then suppresses the popover outright, and every
slash after the first was unreachable.

Only the first slash can be an invocation, so detect the inline shape
first. It requires a whitespace-preceded slash sitting at the caret, so
it can't take over ordinary argument completion (`/personality alic` has
no second slash) — it fires only where completion was already dead.
2026-07-27 22:02:06 -05:00