Skip to content

Buzz QoL bundle for v2026.7.30 - #1

Draft
OthmaneN wants to merge 17 commits into
mainfrom
agent/buzz-qol-v2026.7.30
Draft

Buzz QoL bundle for v2026.7.30#1
OthmaneN wants to merge 17 commits into
mainfrom
agent/buzz-qol-v2026.7.30

Conversation

@OthmaneN

@OthmaneN OthmaneN commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Base

Built from Hermes release v2026.7.30 (commit cc4cab469cd2da24ddedeb68d2f06f9ec3226875).

Included

  • Buzz thread-aware session isolation and canonical root handling
  • Buzz outbound replies and local image uploads stay on the canonical thread root
  • Buzz reaction acknowledgements and configurable reaction emoji
  • Dashboard conversation-management fixes

Overlapping thread-root logic was deduplicated onto NousResearch#74516. The canonical root now scopes Hermes session memory and anchors outbound text/image replies, avoiding nested Buzz subthreads. reply_in_thread is YAML/config only; no extra environment-variable path.

Verification

  • Buzz tests: 102 passed
  • web server tests: 105 passed
  • dashboard tests: 2 passed
  • dashboard typecheck and ESLint
  • Ruff

bbamnesia and others added 17 commits August 1, 2026 01:23
Signed-off-by: bbamnesia <68875027+bbamnesia@users.noreply.github.com>
…e relay

send() forwards metadata["thread_id"] as --reply-to, but this adapter
never sets thread metadata on inbound — every inbound event logs
reply_to_id=None. That value therefore arrives from generic gateway
machinery and can name an event this relay has never seen, so buzz-cli
rejects the entire send:

    error: parent event <id> not found (exit 4)

The message is then lost, because the plain-text fallback in
gateway/platforms/base.py re-sends with the same reply_to and metadata
and varies only the text — it is a fallback for formatting problems, not
for argument-level rejections. Two replies were lost this way within ten
minutes of ordinary use against a hosted relay, in a channel where the
triggering messages were plain top-level posts.

Threading is a presentation detail; losing the message over it is not.
On that specific error, strip --reply-to and retry once. Unrelated
failures are untouched and still surface as before.

The deeper cause is that the adapter does not model Buzz threads at all —
it only ever reports chat_type "group" or "dm", never "thread", so
gateway thread state and Buzz thread state are never reconciled. That is
a design change and belongs in its own issue; this only stops the
mismatch from destroying messages.
Review caught that the first commit only covered text send(). Native
local-file uploads and _standalone_send build the same --reply-to
argument and return the same parent-not-found failure, so cron delivery
(deliver=buzz) and image replies could still be lost outright.

Extract the bounded retry into _send_retrying_unthreaded() and use it
from all three call sites. The helper takes a runner callable returning
(code, stdout, stderr), which lets the adapter's _run_cli and the
module-level _exec_buzz share one implementation without _standalone_send
needing an adapter instance.

Behaviour is unchanged: still a single retry, still only on
parent-not-found, every other failure surfaces as before.

Tests cover the two newly-guarded paths, including that the --file
upload survives the retry.
The Buzz adapter always passes --reply-to with the triggering message's
event id, causing every outbound reply to chain into the source message's
Nostr thread. In multi-turn conversations this produces deeply nested
threads (thread-inside-thread-inside-thread) that hurt readability.

Add a  config option (default: true, backwards-compatible)
that controls whether --reply-to is emitted on send/send_image/standalone
delivery. Set to false to post flat, top-level replies instead.

Config (config.yaml):
  gateway.platforms.buzz.extra.reply_in_thread: false

Or env override:
  BUZZ_REPLY_IN_THREAD=false

Applies to three send paths: send(), send_image(), and _standalone_send()
(cron delivery).
OthmaneN pushed a commit that referenced this pull request Aug 17, 2026
Addresses both review findings on the remote-gateway download PR:

1. Unbounded buffering (finding #1). fetchBuffer / fetchBufferViaOauthSession
   accumulated the entire response (then copied it again via Buffer.concat)
   before saveGatewayFile even opened the save dialog, so a large gateway file
   could exhaust the native process. Both auth paths now stream: once response
   headers arrive the connect timeout is cleared, the filename is derived, the
   save dialog is shown, and the body is piped to the chosen destination with
   backpressure. A read/write error tears down the stream and unlinks the
   partial file. The byte-moving, data-URL decoding, and filename/path helpers
   are extracted into gateway-file-download.ts so they're unit-testable without
   Electron.

2. No fallback for older gateways (finding NousResearch#2). saveGatewayFile required the new
   /api/fs/download route. Desktop and the remote gateway update independently,
   so a gateway predating this PR 404s. Added a 404-only compatibility fallback
   to the existing capped /api/fs/read-data-url route (bounded, so it only
   serves smaller files — enough to keep older backends working).

Tests: gateway-file-download.test.ts covers streaming, backpressure,
error-cleanup (unlink on write/response error), data-URL decoding, filename
derivation (incl. traversal reduction), and 404 detection;
gateway-file-download-transport.test.ts asserts both transports stream (no
whole-body Buffer.concat) and that the 404 fallback is wired. Both registered
in the desktop platform test list. Server-side /api/fs/download tests
(streaming + sensitive-file reject) already pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
OthmaneN pushed a commit that referenced this pull request Aug 17, 2026
…-renders (NousResearch#81726)

The scoped find walker wraps transcript text nodes in <mark> elements that
React does not own. Assistant responses stream through markdown-text.tsx,
which rebuilds the markdown DOM on every delta, and a new message is
appended whenever the assistant answers — so a re-render of a changed
region detaches the marks we inserted, dropping the user's highlights while
the bar stays open.

Watch the captured scope with a MutationObserver and re-wrap only when an
unmarked occurrence of the active query actually reappears. The observer is
gated behind a re-entrancy flag while the walker is mutating, coalesced to
one re-apply per microtask, torn down when the bar closes or the query
clears, and restores the active ordinal so a mid-stream re-render doesn't
reset the user's place to match #1. An append that adds no matching text is
a no-op; re-wrapping only fires when highlights genuinely went stale.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants