mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
v0.5.2
1335
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3e48f1b236 |
chore(release): release Buzz Desktop version 0.5.2 (#3624)
## Buzz Desktop release v0.5.2 ### Changes since v0.5.1: - feat(cli): mirror Desktop mention delivery ([#3330](https://github.com/block/buzz/pull/3330)) ([`7adc46268`](https://github.com/block/buzz/commit/7adc46268d5e93f0b1d4dc8e700af22815dcac1b)) - fix(desktop): deduplicate relay outage notification ([#3579](https://github.com/block/buzz/pull/3579)) ([`66e705492`](https://github.com/block/buzz/commit/66e7054928cc29395f828467c3e8c81b7408dd29)) - fix(desktop): reconcile thread arrivals at bottom ([#3585](https://github.com/block/buzz/pull/3585)) ([`b42a8d447`](https://github.com/block/buzz/commit/b42a8d447e3a2b85b2313dc4fdd123731fd8bba3)) - Improve emoji autocomplete matching ([#3571](https://github.com/block/buzz/pull/3571)) ([`259de6afb`](https://github.com/block/buzz/commit/259de6afbe0cc0d106e57ebdb2323064990e4122)) - Fix shared agent avatar import profiles ([#3578](https://github.com/block/buzz/pull/3578)) ([`324bd6b46`](https://github.com/block/buzz/commit/324bd6b464de5751e12abbd155376046ce3d2afc)) - Fix inline raster avatars in agent catalog ([#3581](https://github.com/block/buzz/pull/3581)) ([`7e9b77f72`](https://github.com/block/buzz/commit/7e9b77f72d82e019a99f074f1c9829be30c57ae1)) - feat(agent): make Gemini and MLflow-route models usable through databricks_v2 ([#3569](https://github.com/block/buzz/pull/3569)) ([`4a1ebf25c`](https://github.com/block/buzz/commit/4a1ebf25c782fc6a68f0a69e6f866f793a259a1f)) **To release:** merge this PR. The tag and build will happen automatically. Signed-off-by: Wes <wesbillman@users.noreply.github.com> |
||
|
|
5aeed7c7a2 |
fix(desktop): discover bun-installed agent CLIs in ~/.bun/bin (#3343)
## Problem `common_binary_paths()` probes mise shims, `~/.local/bin`, volta, asdf, and (further down `resolve_command_uncached`) nvm's default bin dir — but not bun's global bin directory, `~/.bun/bin`. bun's installer appends its bin dir to `~/.zshrc` / `~/.bashrc`, which are **interactive**-only. A login shell never sources them, so `find_via_login_shell()` can't recover the path either. That's the same failure mode already called out in this file for nvm: ```rust // Check nvm's default Node.js bin directory — nvm initializes via // ~/.zshrc (interactive) which is not loaded by a login shell, so // `node`, `npm`, and npm-global shims installed there are otherwise // invisible. ``` So for a GUI-launched desktop app, every rung of the resolution ladder misses a bun-installed CLI: 1. workspace dev dirs — no 2. `command_looks_like_path` — no, presets use bare names 3. Buzz-managed npm/node dirs — no 4. current process PATH — launchd's minimal PATH on a Finder launch 5. `find_via_login_shell` — `.zshrc` not sourced 6. `common_binary_paths()` — **`~/.bun/bin` absent** 7. nvm default bin — no This matters because bun is a common install route for the agent CLIs Buzz targets. Kimi Code in particular ships as an npm package (`@moonshot-ai/kimi-code`), so `bun add -g` puts it at `~/.bun/bin/kimi` — exactly where discovery doesn't look. ## Reproduction On macOS with `codex` and `kimi` installed via bun, launching Buzz from Finder: - Kimi Code shows **"CLI needed"** - both CLIs run fine in an interactive terminal Probing the way `find_via_login_shell` does, in a clean environment: ```console $ env -i HOME=$HOME /bin/zsh -l -c 'command -v -- codex; command -v -- kimi' (nothing) ``` Launching the app with the bun dir on PATH resolves both immediately: ```console $ env PATH="$HOME/.bun/bin:$PATH" /Applications/Buzz.app/Contents/MacOS/buzz-desktop ``` ## Change One entry appended to the home-relative list in `common_binary_paths()`. It goes **last** so it cannot shadow a directory that already resolves — the change can only add resolutions, never alter existing ones. ## Testing `cargo fmt --check` passes. I was not able to run the full `just ci` gate locally: `ring 0.17.14` fails to build in this environment against the macOS 26.2 SDK (`cc` error compiling `p256-nistz.c`), which is unrelated to this change. Relying on CI for the rest — the diff adds one `PathBuf` to an existing `Vec<PathBuf>` and introduces no new API. ## Notes - Related to #3084, which adds `~/.kimi-code/bin` for the same class of GUI-launch discovery failure. That covers Kimi's standalone installer; this covers the bun/npm-global install route. They're complementary — I've left a note on that PR. - Only `~/.bun/bin` is added. bun's global packages live under `~/.bun/install/global/node_modules` but are symlinked into `~/.bun/bin`, so the single directory is sufficient. - Worth noting `~/.bun/bin` contains no `node`/`npm`/`npx`, so appending it can't shadow a system Node toolchain. Signed-off-by: Xule Lin <43122877+linxule@users.noreply.github.com> |
||
|
|
7adc46268d |
feat(cli): mirror Desktop mention delivery (#3330)
🤖 ## Summary Agent-authored mentions currently depend on matching visible `@Name` text to channel profiles. That makes notification delivery ambiguous when names collide or profiles change, and it encourages an extra post-send lookup just to confirm that the intended `p` tags were emitted. This change makes `buzz messages send` mirror Desktop's existing model: the message keeps a readable name in its content while the recipient pubkey is supplied separately. ```bash buzz messages send \ --channel <UUID> \ --content '@Alice could you review this?' \ --mention <alice-hex-or-npub> ``` `--mention` is repeatable. The CLI normalizes and deduplicates explicit pubkeys, merges them with any names it can resolve from the channel, and gives explicit identities priority under the existing 50-mention limit. Before uploading attachments, signing, or publishing, the command checks every resulting pubkey against the channel's current membership: - Members are mentioned normally. - Non-members stop the send and produce an actionable error. - `--allow-non-member-mentions` deliberately sends notifying `p` tags without adding anyone to the channel. Sending a message never changes membership. On success, `mention_pubkeys` is read from the exact signed event and returned with the relay response, so callers can verify the emitted recipients without another query. Managed-agent guidance teaches this single-command mention flow. Desktop mention behavior and the Nostr event schema are unchanged. Forum guidance is intentionally handled separately in #3596. ### Related issue None found. This replaces the earlier guidance-only approach in this PR with the underlying CLI behavior it required. ### Testing - `cargo test -p buzz-sdk` - `cargo test -p buzz-cli` - `cargo test -p buzz-acp` - `cargo test --manifest-path desktop/src-tauri/Cargo.toml` --------- Signed-off-by: npub1fdupjvyregj3z2tx7gx5x6py04zw89jm5usef9lyea4f3vcgh8qq9zgkdz <4b78193083ca25112966f20d4368247d44e3965ba7219497e4cf6a98b308b9c0@buzz.block.builderlab.xyz> Signed-off-by: npub13n66s06epmqf2kc3v373ez8hj65cuzyvxzjf93vwpervxqn2u7jq2qd9je <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz> Co-authored-by: npub1fdupjvyregj3z2tx7gx5x6py04zw89jm5usef9lyea4f3vcgh8qq9zgkdz <4b78193083ca25112966f20d4368247d44e3965ba7219497e4cf6a98b308b9c0@buzz.block.builderlab.xyz> Co-authored-by: npub13n66s06epmqf2kc3v373ez8hj65cuzyvxzjf93vwpervxqn2u7jq2qd9je <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz> |
||
|
|
66e7054928 |
fix(desktop): deduplicate relay outage notification (#3579)
🤖 ## Summary - Keep the relay reconnect notification dismissed across repeated connection retries during one continuous outage. - Re-arm the notification after recovery or relay lifecycle replacement, including switches between communities that use the same relay URL. - Preserve the existing dedicated path for authentication and other application-level errors. ### How an outage is tracked The AppShell-owned relay-card hook treats an outage as one contiguous runtime episode rather than assigning it a persisted ID. A hook-local `outageActiveRef` is armed by the first qualifying unreachable/degraded observation. While it is armed, intermediate retry states (`connecting`, `reconnecting`, `stalled`, and `disconnected`) belong to that same episode, so retry churn cannot clear dismissal or emit another notification. The hook receives the same lifecycle identity used by community initialization: community ID plus `reinitKey`. This distinguishes multiple communities even when they share a relay URL, and it also changes when the active community is explicitly reinitialized. The latch and dismissal are reset when that identity changes or when the relay singleton reports its authoritative `idle` teardown state. A successful `connected` state also closes the episode and re-arms the next outage. These boundaries deliberately bias toward re-notifying rather than suppressing a later outage: recovery, community switch/reinit, or relay teardown cannot leave the hook stuck believing an old outage is still active. No outage state is persisted beyond the mounted hook lifecycle. ### Related issue None found. ### Testing - `pnpm --dir desktop typecheck` - `pnpm --dir desktop test` — 3,769 passed - `pnpm --dir desktop check` - `pnpm --dir desktop build:e2e` - `pnpm --dir desktop exec playwright test tests/e2e/sidebar-relay-card.spec.ts --project=integration` — 11 passed --------- Signed-off-by: Larry <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz> Co-authored-by: npub13n66s06epmqf2kc3v373ez8hj65cuzyvxzjf93vwpervxqn2u7jq2qd9je <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz> |
||
|
|
b42a8d447e |
fix(desktop): reconcile thread arrivals at bottom (#3585)
## Summary - reconcile stale native-scroll anchors when a reply arrives at the physical floor - clear the thread new-message affordance instead of incrementing it from stale cached state - preserve the existing mid-history path and add direct lifecycle regression coverage ## Why PR #3411 fixed geometry-driven reconciliation, but the reply-arrival branch still trusted a cached `message` anchor without checking the rendered position. Native anchoring could return a short thread to the floor without another scroll/resize callback, then the next reply incremented the pill anyway. ## Verification - Desktop checks passed - Desktop typecheck passed - focused lifecycle test passed (6/6) - push hook full Desktop unit suite passed (3,770/3,770) - `git diff --check` passed Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
259de6afbe |
Improve emoji autocomplete matching (#3571)
## Summary - Show all colon emoji autocomplete matches - Rank exact and prefix shortcodes before weaker matches - Add a regression test and screenshot ## Validation - `pnpm test` - `pnpm build` - `pnpm exec playwright test --project=smoke tests/e2e/custom-emoji.spec.ts --grep "exact standard shortcode"` - `just desktop-tauri-clippy` Native Tauri tests were attempted but could not link because the local disk filled during compilation. --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> |
||
|
|
324bd6b464 |
Fix shared agent avatar import profiles (#3578)
> Carl is updating this pull request on Wes's behalf. ## Summary - upload an embedded raster avatar through the existing authenticated media pipeline before minting or persisting an imported shared agent - store and publish only the resulting hosted URL so agent kind:0 profiles remain within content limits ## Root cause Snapshot import recovered raster avatar pixels as a large inline base64 data URL. That value was persisted and placed into the agent's kind:0 profile. The relay rejected the oversized profile, so other clients could not resolve the imported agent's avatar. ## Scope This is intentionally the forward fix only. It changes two Desktop files and does **not** add migration or reconciliation behavior for previously imported agents. Existing affected imports must be re-imported or fixed manually. ## Validation - successful pre-push Desktop suite: 1,863 passed, 14 ignored, 0 failed - all pre-push Rust/Desktop gates green, including all-target clippy - valid >256 KiB PNG import → production MIME detection/sanitization → bounded signed kind:0 containing only the hosted URL - upload failure, malformed data, and URL-only avatar cases covered - independent fresh review by Princess Donut: clean, no blocking findings Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
7e9b77f72d |
Fix inline raster avatars in agent catalog (#3581)
## Summary - render existing shared personas whose catalog avatar is a bounded inline PNG, JPEG, GIF, or WebP data URL - keep rejecting arbitrary, malformed, unsupported, and oversized `data:` URLs - preserve hosted relay URLs as the forward format; catalog browsing remains read-only ## Root cause Paul's live shared kind:30175 head contains a 144,878-character `data:image/png;base64,...` avatar. The catalog projection accepted HTTP(S) URLs and bounded percent-encoded SVG emoji avatars only, so it projected Paul's avatar to `null` before `ProfileAvatar` rendered it. The owner still saw the local persona avatar, producing the reported owner/viewer mismatch. This patch accepts only four raster MIME types with strict base64 shape and a 256 KiB total URL cap at the existing catalog parsing boundary. It repairs already-signed heads such as Paul without viewer-side uploads or publication side effects. Hosted media remains the canonical forward path. #3578 uploads inline raster avatars during snapshot import, preventing the known source from creating future inline persona/profile values; existing signed catalog heads still need this compatibility path until their owners republish. ## Agent instruction finding The catalog publishes `AgentDefinition.system_prompt` verbatim as the user-authored **Agent instruction**, as documented by the sharing UI and NIP-AP. No Buzz base/core/runtime prompt is concatenated in the publish, catalog, or import path. This PR therefore does not remove authored instructions and accidentally strip copied agents of their behavior. ## Validation - targeted `personaCatalogRelay.test.mjs`: 24 passed - Desktop typecheck: passed - pre-push Desktop frontend suite: 3,771 passed - pre-push Desktop checks: passed - `git diff --check`: passed - independent review: no blockers, 9.4/10 Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
a13085e9ac |
chore(release): release Buzz Desktop version 0.5.1 (#3566)
## Buzz Desktop release v0.5.1 ### Changes since v0.5.0: - perf(desktop): move observer-feed archive and decrypt commands off main thread ([#3415](https://github.com/block/buzz/pull/3415)) ([`294c8c821`](https://github.com/block/buzz/commit/294c8c821de51442a8c384c0bdb66b1a10224ca0)) - fix(desktop): preserve shared agent fidelity ([#3553](https://github.com/block/buzz/pull/3553)) ([`f7a3988ba`](https://github.com/block/buzz/commit/f7a3988ba13b590d9a55a7e8413fc3fb5ffbef18)) - feat(agent): route Claude/GPT model families to their native gateway wire ([#3538](https://github.com/block/buzz/pull/3538)) ([`6438dedf8`](https://github.com/block/buzz/commit/6438dedf83a9dbe1853e484326911bf6c7f1618c)) - Refine community invite limits ([#3529](https://github.com/block/buzz/pull/3529)) ([`24d90d128`](https://github.com/block/buzz/commit/24d90d1280a9325c6cbcf8eea30ac54db5afd2cb)) - feat(agent): fix Anthropic prompt caching with Databricks (+ MCP proxy/TLS passthrough) ([#3463](https://github.com/block/buzz/pull/3463)) ([`c405ad1d4`](https://github.com/block/buzz/commit/c405ad1d4b1da061c11b3d26761252d41dcc62d3)) - feat: add explicit entry for claude-opus-5 in model config ([#2831](https://github.com/block/buzz/pull/2831)) ([`90e058ebf`](https://github.com/block/buzz/commit/90e058ebf68137e048a409aec6616519379ff726)) - fix(desktop): clear stale thread new-message pill ([#3411](https://github.com/block/buzz/pull/3411)) ([`55a3ed7b9`](https://github.com/block/buzz/commit/55a3ed7b9217cee5b23e0a5441947dc929b2a38c)) - fix(ci): ratchet file sizes against the base tree ([#3352](https://github.com/block/buzz/pull/3352)) ([`9227bdf58`](https://github.com/block/buzz/commit/9227bdf58ad6664ae3c1078888f2181ec19c4da4)) - feat(desktop): apply WebKit rendering workarounds at startup on Linux ([#3271](https://github.com/block/buzz/pull/3271)) ([`3ece4461d`](https://github.com/block/buzz/commit/3ece4461df8a7b9663a8e68327483b8377d4086d)) - fix(desktop): stabilize flaky DM expansion E2E ordering assertions ([#2004](https://github.com/block/buzz/pull/2004)) ([`913d564ce`](https://github.com/block/buzz/commit/913d564ce0f35924291bf3eeab6508517a6d8d1f)) - fix(desktop): paint community rail full height ([#3382](https://github.com/block/buzz/pull/3382)) ([`1d3b810ad`](https://github.com/block/buzz/commit/1d3b810ad70d6325718ed91e723f32c4a376d5e1)) - feat(desktop): add custom harness inline from agent dialogs ([#3252](https://github.com/block/buzz/pull/3252)) ([`b0503d80c`](https://github.com/block/buzz/commit/b0503d80c298b1ece3b0a43b41d316829a3379e7)) - feat(desktop): refine agent catalog sharing ([#2439](https://github.com/block/buzz/pull/2439)) ([`a35771fc4`](https://github.com/block/buzz/commit/a35771fc441cdc3c6f517f419037206783b502d2)) - fix(desktop): keep drafts out of the Inbox All view ([#3217](https://github.com/block/buzz/pull/3217)) ([`3afa129ee`](https://github.com/block/buzz/commit/3afa129ee785cc74d921d0ba969254a8255c4cc0)) - fix(desktop): restore the inbox icon in the sidebar ([#3341](https://github.com/block/buzz/pull/3341)) ([`00ede2e7a`](https://github.com/block/buzz/commit/00ede2e7aa7eb95571b7db3ebbd163adbf6cf74e)) - fix(desktop): gate codex-acp on a minimum supported version ([#3254](https://github.com/block/buzz/pull/3254)) ([`4e3998f36`](https://github.com/block/buzz/commit/4e3998f36e36d68b9a93dcbd85f0864450bb8f5f)) - feat(cli): add users set-status command for NIP-38 profile status ([#3253](https://github.com/block/buzz/pull/3253)) ([`60158fce3`](https://github.com/block/buzz/commit/60158fce3e670f11bb35d42627857ccaea50ff06)) - fix(composer): scope multiline block formatting ([#3246](https://github.com/block/buzz/pull/3246)) ([`5457c947a`](https://github.com/block/buzz/commit/5457c947a74f5ba4b979f9c6411aa7626a858387)) **To release:** merge this PR. The tag and build will happen automatically. Signed-off-by: Wes <wesbillman@users.noreply.github.com> |
||
|
|
294c8c821d |
perf(desktop): move observer-feed archive and decrypt commands off main thread (#3415)
Opening the agent observer feed could beachball the app. In Tauri 2, a sync (`pub fn`) command body runs on the **main thread** — only `async fn` commands run on the runtime pool. Five commands on the observer-feed open path were sync, so panel open ran SQLite I/O and secp256k1 work on the macOS main thread: | Command | Main-thread work | |---|---| | `decrypt_observer_event` | Schnorr ID + signature verify, then NIP-44 decrypt — once per frame | | `read_archived_observer_events_for_channel` | Opens the archive DB, runs the channel-index JOIN, returns up to 200 raw JSON blobs per page | | `read_unindexed_observer_rows` | Opens the DB, returns **all** not-yet-indexed kind-24200 rows in one shot | | `index_observer_channel_id` | Opens the DB, loops N upserts | | `delete_save_subscription` | Opens the DB, one delete | Eager hydration loads up to 10 pages × 200 frames on panel open, so that's up to 10 main-thread DB reads plus up to 2,000 sequential verify+decrypt calls before any scrolling. The one-shot backfill makes it worse on the first open after history accumulates: one read of every unindexed row, a decrypt per row, then a batch upsert — all on the main thread, and all proportional to archive size. The four archive commands now route their DB work through the existing `run_archive_db_task` helper (`spawn_blocking` + `open_db`), matching `list_save_subscriptions`, `read_archived_events`, and `archive_events` directly around them. `decrypt_observer_event` becomes `async fn` + `tauri::async_runtime::spawn_blocking`, with `state.signing_keys()` extracted before the spawn since `State` is not `Send` — the same pattern `sign_event` uses from #1222. No frontend changes: `invoke` is already promise-based, so the TS wrappers in `tauriArchive.ts` and `tauriObserver.ts` are unchanged. This removes the freeze, not the work. Eager hydration still takes the same wall time — the feed shows a loading state instead of blocking the UI. Batching the per-frame decrypt IPC (2,000 round-trips into one command) would cut the latency itself; that's deliberately out of scope here. Signed-off-by: Will Pfleger <pfleger.will@gmail.com> |
||
|
|
f7a3988ba1 |
fix(desktop): preserve shared agent fidelity (#3553)
## Summary Fixes two distinct fidelity failures in direct agent sharing: - The sender now puts the same effective avatar shown on the agent card into People-share and file-export snapshot PNGs, including profile/kind:0 fallback avatars. - The importer now persists the visible PNG body as the portable avatar instead of ignoring it in favor of sender-local manifest references. - Export materializes inherited runtime, provider, and model identifiers verbatim, while preserving explicit definition values. It does not translate or substitute configuration for a different recipient setup. - Sharing waits for a profile-only fallback avatar query, preventing an early-click race. The PNG import path keeps the existing safety invariant: decode is capped at 2048×2048 / 32 MiB and re-encoded avatars above the 2 MiB inline limit fall back to the manifest reference. The exact transparent 1×1 no-avatar placeholder is ignored. The original Tyler↔Wes screenshot demonstrates both stages: Wren's attachment had an avatar that disappeared after **Add agent** (receiver/import failure), while Pinky's attachment was already blank (sender/projection failure). ### Related issue N/A — reported and traced in the linked Buzz conversation. ### Testing - `cargo test --manifest-path desktop/src-tauri/Cargo.toml commands::personas::snapshot` — 57 passed - `pnpm exec tsc --noEmit` - Biome check on changed frontend/E2E files - Pre-push hooks: - desktop check - desktop tests - desktop Tauri tests — 1853 passed, 14 ignored - file-size ratchet The People-share E2E regression asserts that a profile-only avatar reaches `avatarPngDataUrl` in the real encode command payload. --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
24d90d1280 |
Refine community invite limits (#3529)
## Summary - Simplify the community invite dialog around link sharing. - Add matching expiry and use-limit dropdowns, with sensible preset use caps. - Cover the default unlimited and selected-limit invite payloads. ## Validation - `pnpm -C desktop run build:e2e` - `pnpm -C desktop exec playwright test tests/e2e/invite-link-copy.spec.ts tests/e2e/invites-settings-screenshots.spec.ts --project=smoke` Signed-off-by: kenny lopez <klopez4212@gmail.com> |
||
|
|
90e058ebf6 |
feat: add explicit entry for claude-opus-5 in model config (#2831)
Fixes #2787 - Added `claude-opus-5` to `config.rs` model classification and adaptive effort helpers. - Updated fixture test configurations to cover `claude-opus-5`. - Verified with `cargo test` and JS unit tests. Signed-off-by: Apurva Shaw <apurvashaw@Apurvas-MacBook-Air.local> Co-authored-by: Apurva Shaw <apurvashaw@Apurvas-MacBook-Air.local> |
||
|
|
55a3ed7b92 |
fix(desktop): clear stale thread new-message pill (#3411)
## Summary - reconcile anchored-scroll state when passive layout changes put a thread at its physical floor - route thread composer-padding growth and shrink through the same hook-owned settlement path - preserve pinned thread targets while clearing stale new-message state ## Root cause Thread bottom state was updated primarily by native `scroll` events. Deferred replies, viewport changes, and composer-overlay padding can finish changing geometry after the user's last scroll—or after the initial open pin—without another scroll event. The thread could visibly reach the floor while `isAtBottom` and `newMessageCount` remained stale, leaving the “N new messages” pill visible. ## Verification - `pnpm check` - `pnpm typecheck` - `pnpm test` — 3,768 passed - push hook: branch-skew, Desktop check, and Desktop full unit suite passed Signed-off-by: Wes <wesbillman@users.noreply.github.com> |
||
|
|
9227bdf58a |
fix(ci): ratchet file sizes against the base tree (#3352)
## Summary - replace the whole-tree file-size gate with a stateless differential ratchet - allow inherited files over 1,000 lines to hold or shrink, but never grow - delete the 44-entry numeric override ledger and run the same policy across Desktop, Web, and Mobile CI - fail closed when the local base cannot be resolved and cover policy, Git status parsing, and base resolution in unit tests This removes the shared mutable policy state that caused unrelated PRs to fail after neighboring merges. It does **not** by itself prevent two stale green PRs from becoming invalid when combined; that requires merge queue or up-to-date branch enforcement. ### Related issue None found. This follows the design discussion in the linked Buzz channel. ### Testing - `node --test scripts/check-file-sizes-core.test.mjs` (6/6) - Desktop, Web, and Mobile ratchet entrypoints - `just desktop-check` - `just web-check` - Mobile analysis - `git diff --check` The repository pre-push suite also exposed an unrelated existing Mobile widget failure in `ChannelDetailPage keeps follow mode off while a tall newest message stays visible`; it reproduces in isolation and this branch does not touch Mobile widget behavior. Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
3ece4461df |
feat(desktop): apply WebKit rendering workarounds at startup on Linux (#3271)
On some Linux GPU/driver/compositor combinations, WebKitGTK's dmabuf renderer aborts the web process during startup, so Buzz comes up with no window at all and the user has no way to fix it. Setting `WEBKIT_DISABLE_DMABUF_RENDERER=1` avoids the abort by falling back to the shared-memory buffer path. WebKit reads each of its rendering variables exactly once per process, so the choice has to be made before anything initializes — there is no runtime toggle and no second chance later in the same process. This decides up front from two cheap preflight signals rather than reacting to a crash: - **NVIDIA GPU** — any DRM device under `/sys/class/drm` reporting PCI vendor `0x10de`, the driver family behind most upstream reports. - **AppImage** — the `APPIMAGE` environment variable. linuxdeploy's AppRun hook pins `GDK_BACKEND=x11`, and the dmabuf renderer buys nothing on that XWayland path. Either signal disables the dmabuf renderer. Neither signal leaves the environment untouched. ## Escape hatches `--safe-rendering` forces the safest configuration for one launch — `WEBKIT_DISABLE_DMABUF_RENDERER` plus `WEBKIT_DISABLE_COMPOSITING_MODE` — for a machine neither signal recognises. Any user assignment of a variable this module may set stands the heuristic down **wholesale**. Presence is the test, not truthiness, so `VAR=0` and `VAR=` both count: a user asking for the dmabuf renderer *on* gets it, even on a machine the heuristic would have opted out. `--safe-rendering` against such an assignment is refused with a diagnostic naming both the assignment and the key to unset, and exits non-zero — the flag and the environment are two incompatible answers to one question, and neither is guessed. ## Placement `webkit_rendering::apply()` runs at the top of `fn main()`, before `buzz_lib::run()`. That is the only point where the process is still single threaded with no GTK object alive, which is what makes `std::env::set_var` sound; the module doc and the call site both say so. The whole module is `#[cfg(target_os = "linux")]` — macOS and Windows compile none of it. The decision is a pure function of argv, an injected environment lookup, and an injected DRM root, so all of it is unit-testable without mutating the process environment. Closes #2338. Upstream: [tauri#9394](https://github.com/tauri-apps/tauri/issues/9394). Same approach and same variable as [clash-verge-rev](https://github.com/clash-verge-rev/clash-verge-rev/blob/main/src-tauri/src/utils/linux/workarounds.rs) `workarounds.rs` and [screenpipe](https://github.com/screenpipe/screenpipe/blob/main/apps/screenpipe-app-tauri/src-tauri/src/linux_webkit_env.rs) `linux_webkit_env.rs`. Signed-off-by: Will Pfleger <pfleger.will@gmail.com> |
||
|
|
913d564ce0 |
fix(desktop): stabilize flaky DM expansion E2E ordering assertions (#2004)
## Summary Fixes 4 flaky DM expansion E2E tests in Desktop Smoke shard 1 that were failing non-deterministically on CI (also reproducing on `main` at run `29526844596`). **Failing tests:** - `channels.spec.ts:652` — creates the DM before preparing a persona mention - `channels.spec.ts:760` — routes an agent mention from an existing DM to the expanded conversation - `channels.spec.ts:815` — routes a relay-agent mention from an existing DM to the expanded conversation - `channels.spec.ts:940` — drops an expanded DM after the first message fails ## Root Cause Race condition: under fast CI execution, mock command completions (create_managed_agent, open_dm) can resolve in non-deterministic order, causing assertions to observe stale or mid-transition state. ## Fix - **:652** — Move the `new-message-recipient-popover` hidden assertion after `chat-title` settles (both names present), so it runs post-transition rather than mid-transition. - **:760, :940** — Add `createManagedAgentDelayMs: 100` to ensure persona provisioning doesn't collapse into the same tick as the expanded-DM open/start sequence. - **:815** — Add `openDmDelayMs: 100` so the two open_dm calls resolve in deterministic order. ## Validation All 4 tests pass with `--repeat-each=3` (12/12 green) locally. Biome lint clean. ## Scope Test-only change: 12 insertions, 1 deletion in `desktop/tests/e2e/channels.spec.ts`. --- Investigated by Ferret, reviewed by Grumplestiltzkin. Signed-off-by: Cameron Hotchkies <chotchkies@block.xyz> Co-authored-by: Goose <opensource@block.xyz> |
||
|
|
1d3b810ad7 |
fix(desktop): paint community rail full height (#3382)
## Summary - paint the community rail across the full app height instead of exposing the parent background through external margins - preserve the existing community-button alignment and balanced horizontal gutters by moving vertical spacing inside the rail - update the rail geometry coverage to require full-height paint ownership ## Root cause PR #2972 aligned the rail box with the inset content by adding top and bottom margins to the `bg-sidebar` element. Margins are outside the painted box, so flat light and dark themes exposed a differently colored app background above and below the rail. ## Validation - pre-push `desktop-check` - pre-push desktop unit suite: 3,751 passed - `git diff --check` Local Playwright/E2E was not run; CI owns the full browser matrix. Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
b0503d80c2 |
feat(desktop): add custom harness inline from agent dialogs (#3252)
Registering a custom ACP harness works today, but only from Settings → Agents. Anyone whose first touchpoint is "New agent" has no way to discover the custom path — the dropdown just lists the baked-in presets plus whatever was registered earlier. This adds an inline "Add custom harness…" entry to the harness dropdown in all three agent surfaces: create, edit-definition (`AgentDefinitionDialog`), and instance edit (`AgentInstanceEditDialog`). The entry is a sentinel option (`ADD_CUSTOM_HARNESS_VALUE`, NUL-prefixed so it can never collide with a real harness id — backend ids match `[a-z0-9_][a-z0-9_-]*`), mirroring the `CUSTOM_ENTRY_ID` trick already used in `HarnessCatalogDialog`. Picking it never writes into form state; it opens `AddCustomHarnessDialog`, a thin modal wrapper hosting the existing `CustomHarnessForm` in `chromeless` mode. `CustomHarnessForm`'s `onSaved` now carries the saved `definition.id` (the form may rewrite it); the two existing call sites ignore the argument, so their behavior is unchanged. Selection after save is deferred rather than immediate. `usePendingHarnessSelection` holds the saved id until the runtime catalog actually publishes it via discovery, then selects it exactly once — so the dialog never selects an id it cannot render, and back-to-back registrations resolve correctly. The wait is scoped to the owning dialog's `open` state: both host dialogs stay mounted when closed, so an unpublished id is dropped on close rather than selecting into reset form state when discovery later catches up. Selection is routed through each dialog's normal dropdown change handler, so provider/model reset (and command pinning in the instance dialog) behave identically to a hand-picked harness. Dismissing the modal leaves the previous selection untouched. `AgentInstanceEditDialog`'s existing "Custom command" option is a different feature (ad-hoc command override vs. a registered reusable harness) and is untouched. Coverage is 16 unit tests in `addCustomHarness.test.mjs` (real React mount, following the existing `.test.mjs` pattern) plus 4 Playwright specs in `inline-custom-harness.spec.ts` covering all three surfaces end-to-end. Both suites were mutation-verified: treating the sentinel as a real selection, selecting before the catalog publishes, never clearing the pending id, ignoring the dialog's open state, and reversing latest-save-wins each turn the unit tests red; reverting the two dialog diffs turns all four e2e specs red. The `check-file-sizes.mjs` overrides for the two dialogs are ratcheted to their exact new counts (1048 and 1229) — verified tight in both directions, N passes and N−1 fails, so no headroom is introduced. --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> |
||
|
|
a35771fc44 |
feat(desktop): refine agent catalog sharing (#2439)
## Summary - add custom-agent catalog sharing and hide built-ins from discovery - let owners publish later catalog updates from Share or while saving edits — the save always persists locally, and the publish reports `published` or `queued` (flushed automatically once the relay is reachable again) - preserve agent type, model, and runtime across snapshot import/export - simplify agent and team entry points and tighten catalog layout - migrate the legacy global retention queue into the owner's active scope so pending catalog publishes survive the upgrade - keep the agent list and edits usable in recovery mode by degrading to unshared projections when scope resolution or the retention DB fails - track catalog provenance on copied personas, so adding an already-added foreign agent resolves to the existing copy instead of creating a duplicate - scope inbound persona events to the community relay they arrived on - page the catalog read past the relay's 1,000-row query clamp - unify the share dialog's memory-level choice into a single "What's included" selector that drives both DM-send and copy-link delivery (all six combinations preserved), group the delivery rows above the option rows, and label the catalog toggle "Not shared" / "Shared" - keep emoji avatars on catalog entries — they persist as inline percent-encoded SVG, which the catalog projection's http(s)-only URL guard used to drop, so a shared agent showed initials instead of its avatar - drop the "Active in communities" card from Agents settings, superseded by the per-channel runtime controls in the members sidebar ## Screenshots ### Agent actions  ### Team avatar stack  ### Catalog sharing  ### Publish while editing  ### Publish from Share  ### Catalog details  --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Will Pfleger <pfleger.will@gmail.com> |
||
|
|
3afa129ee7 |
fix(desktop): keep drafts out of the Inbox All view (#3217)
## Summary Drafts were showing up in the Home Inbox **All** view, mixed in with messages and reminders (reported in `#buzz-bugs`). Drafts are private composer state, not inbox activity — they now appear only under the dedicated **Drafts** filter. ## Changes - **`inboxListRows.ts`** — drop the `draft` row variant from `buildInboxListRows`; the mixed view builds only `inbox` + `reminder` rows. - **`InboxListPane.tsx`** — remove the draft branch of the All-view render path; `PersonalItemRow` now renders reminders only. - **`useHomePersonalInbox.ts`** — stop enabling draft selection (and its root-status relay probing) for the mixed view; draft selection is scoped to the Drafts filter. - Drafts filter behavior is unchanged: the filter badge count, `DraftsPanel` list, and `DraftDetailPane` all still work. ## Testing - `pnpm test` (desktop unit suite): 3697 passed, 0 failed. - `pnpm exec biome check src/features/home tests`: clean. - Updated `inboxListRows.test.mjs` for the two-variant row model. - Updated the e2e test (`channels.spec.ts`) to assert All never lists drafts and that the draft is still reachable under the Drafts filter. - Added `drafts-all-fix-screenshots.spec.ts` capturing both states (screenshots below). ### All view — draft is gone, messages/reminders unaffected  ### Drafts filter — the draft is still listed and editable  Signed-off-by: Thomas Petersen <thomasp@squareup.com> |
||
|
|
00ede2e7aa |
fix(desktop): restore the inbox icon in the sidebar (#3341)
## Why The Inbox surface was briefly renamed to **Activity** during #2045 and picked up a bell icon to match. The name was reverted to **Inbox** before merge, but the icon was not. A bell says "notification tray." Inbox is a destination — a focused, conversation-oriented place to catch up on work relevant to you, including drafts and reminders that have nothing to do with notifications. The glyph should say that. ## What changed - Swap the sidebar entry from Lucide `Bell` to Lucide `Inbox`. - Assert the icon in `inbox-refactor-screenshots.spec.ts`. Nothing pinned it before, which is exactly how it drifted through a rename. This also brings desktop back in line with mobile, which already uses `LucideIcons.inbox300` / `inbox500` for the same destination. ## Deliberately unchanged The bell on **reminder** rows in the list pane (`InboxListPane.tsx`, reminders → bell, drafts → file) stays. A bell is the right glyph for a reminder; that one was never about the surface's identity. ## Verification - The new assertion is a real guard, not a no-op: with `Bell` restored the test fails with `Expected: 1, Received: 0` on `svg.lucide-inbox`. Confirmed before committing. - `biome` and `tsc` clean. - Playwright smoke: `inbox-refactor-screenshots` 4 passed; `smoke`, `navigation`, `channels`, `sidebar-more-unread-overlap`, `home-collapsed-top-chrome`, `workspace-rail` — 107 passed, 1 skipped. - Screenshot below is the regenerated `02-current-controls` shot from the spec. Signed-off-by: Clay Delk <clay.delk@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
4e3998f36e |
fix(desktop): gate codex-acp on a minimum supported version (#3254)
The codex adapter version gate accepted any `major >= 1`, so a 1.x `codex-acp` older than the version that fixes outbound relay access for `buzz` CLI subprocesses classified as `Available` and was never offered a reinstall. Only the 0.16.x `@zed-industries/codex-acp` adapter — which fails `--version` outright — was caught. `probe_codex_acp_version` now returns the full `(major, minor, patch)` triple and `codex_adapter_availability` compares it against a new `MIN_CODEX_ACP_VERSION` floor of `1.1.7`, the current npm latest. An adapter below the floor classifies as `AdapterOutdated`, which routes it through the existing uninstall-then-install reinstall plan. The parse requires exactly three numeric dot-separated components. Partial versions (`1.2`) and prerelease tags (`1.2.0-rc1`) return `None` and therefore classify as `AdapterOutdated` — a version Buzz cannot compare against the floor fails closed, offering a reinstall rather than running an adapter of unknown vintage. Both the floor's bump policy and the strict-parse behavior are stated in doc comments rather than left implicit. Supersedes [#3097](https://github.com/block/buzz/pull/3097) by @Bharathchinneni, whose semver floor and behavior tests this carries. That PR could not land as written: the two `probe_codex_acp_major_version` compatibility wrappers it kept had no non-test callers, which is a hard `clippy -D warnings` failure. The wrappers are deleted here and their call sites collapsed onto `probe_codex_acp_version`. Signed-off-by: Will Pfleger <pfleger.will@gmail.com> |
||
|
|
5457c947a7 |
fix(composer): scope multiline block formatting (#3246)
**Category:** fix **User Impact:** Composer block formatting now applies to the intended line or selection without collapsing multiline content. **Problem:** Block formatting from a Shift+Enter line could convert the entire draft, selected visual lines could collapse into one list item, and code conversion could lose line breaks. **Solution:** Scope caret formatting to its hard-break-delimited line and normalize explicit selections for the destination block type while preserving neighboring content and visual line boundaries. <details> <summary>File changes</summary> **desktop/src/features/messages/lib/selectionBlockFormatting.ts** Scopes collapsed-caret block actions to the active visual line and normalizes multiline selections for lists and code blocks. **desktop/src/features/messages/lib/selectionBlockFormatting.test.mjs** Adds unit coverage for caret-line isolation across line positions and selection directions. **desktop/src/features/messages/ui/FormattingToolbar.tsx** Routes list, quote, and code-block actions through the selection-aware formatting transaction. **desktop/tests/e2e/composer-selection-formatting.spec.ts** Covers caret-only formatting, multiline list conversion, list-to-code conversion, preserved hard breaks, Markdown output, and backward selections. </details> ## Reproduction steps 1. In the desktop composer, enter several lines using Shift+Enter and place the caret on one line. 2. Apply a bullet list, ordered list, quote, or code block; only the caret line should change. 3. Select several Shift+Enter lines and apply a list; each visual line should become its own item. 4. Select several list items and apply Code block; they should become one multiline code block while unselected neighbors remain intact. 5. Select several Shift+Enter lines and apply Code block; each line break should remain visible. ## Screenshots/Demos <img width="508" height="222" alt="Screen Recording 2026-07-27 at 5 29 19 PM" src="https://github.com/user-attachments/assets/35640dea-0cfb-44f1-9b0b-a993c69cb55f" /> Expected multiline code-block result: https://buzz.block.builderlab.xyz/media/d2e2668093af3b67d896a32e9799daccd236da9fc9e24ec56ddb4ebf7d01dd96.png --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Co-authored-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz> |
||
|
|
4a977c588a |
chore(release): release Buzz Desktop version 0.5.0 (#3213)
## Buzz Desktop release v0.5.0 ### Changes since v0.4.26: - feat(invites): add use-limited invite links ([#3141](https://github.com/block/buzz/pull/3141)) ([`d500c2d5c`](https://github.com/block/buzz/commit/d500c2d5cf5d9aabe0ca4ebebfcafdbe5f5b7fd3)) - fix(node): bump Buzz-supplied Node runtimes past OpenClaw's >=24.15.0 floor ([#3218](https://github.com/block/buzz/pull/3218)) ([`98a7b1334`](https://github.com/block/buzz/commit/98a7b1334823ee0be3e3fa5cab7a2e349e438dab)) - fix(desktop): preserve thread anchor through layout reflow ([#3212](https://github.com/block/buzz/pull/3212)) ([`9810d8545`](https://github.com/block/buzz/commit/9810d8545937329f229ff40d8a19edc9e3e325c1)) - feat(search): parse from:/in:/after:/before: and pass them in the filter ([#2871](https://github.com/block/buzz/pull/2871)) ([`cb2a265b5`](https://github.com/block/buzz/commit/cb2a265b5399426e808461c1a16713754c593258)) - fix(desktop): fetch join policies through native networking ([#2862](https://github.com/block/buzz/pull/2862)) ([`0019f8076`](https://github.com/block/buzz/commit/0019f80765e96f056e81b57789b8b5fb80936f72)) - fix(desktop): republish agent identity records when a persona rename propagates ([#2607](https://github.com/block/buzz/pull/2607)) ([`7ca0bbd94`](https://github.com/block/buzz/commit/7ca0bbd946fd82a7008132f94d069a97bb53f94b)) - fix(desktop): keep project Inbox previews compact ([#3193](https://github.com/block/buzz/pull/3193)) ([`de1396050`](https://github.com/block/buzz/commit/de13960505fd798070e177cb33b1663100ac06bb)) - Inbox refactor ([#2045](https://github.com/block/buzz/pull/2045)) ([`2bd4c24b7`](https://github.com/block/buzz/commit/2bd4c24b71335e7ce272ec6de6491f7f37f4b20d)) - Fix composer selection formatting and drop overlay ([#3172](https://github.com/block/buzz/pull/3172)) ([`99da5b7eb`](https://github.com/block/buzz/commit/99da5b7ebb19e26453e075bfb949672122b31be3)) - Refine pending message status ([#3153](https://github.com/block/buzz/pull/3153)) ([`75588eaff`](https://github.com/block/buzz/commit/75588eaff2354d620e554c055b80ec83735ddb0a)) - fix(desktop): recover full local storage on startup ([#3182](https://github.com/block/buzz/pull/3182)) ([`174c38e4b`](https://github.com/block/buzz/commit/174c38e4bd1ed8498641546bc4fcb6d5a4c9cede)) - fix(desktop): keep collapsed table separators out of spoilers ([#3169](https://github.com/block/buzz/pull/3169)) ([`4d8b676bb`](https://github.com/block/buzz/commit/4d8b676bb283a1917cec5850c3b7327fe122b0c1)) - feat(desktop): redesign agent runtime settings ([#3093](https://github.com/block/buzz/pull/3093)) ([`d98da7389`](https://github.com/block/buzz/commit/d98da7389e60cfbd79b219aa411449fe2e53a18a)) - fix(desktop): use forward slashes for git credential.helper on Windows ([#3023](https://github.com/block/buzz/pull/3023)) ([`899531684`](https://github.com/block/buzz/commit/8995316844f7ad50552fbae67fbd35119262796f)) - chore(desktop): add AgentCreationPreview file-size override to unblock main CI ([#3154](https://github.com/block/buzz/pull/3154)) ([`b92a1f4bf`](https://github.com/block/buzz/commit/b92a1f4bf400e7da5ab7a010cdd81a69497d8191)) - fix(desktop): make the test loader work on Windows ([#2758](https://github.com/block/buzz/pull/2758)) ([`8bb43d519`](https://github.com/block/buzz/commit/8bb43d51912894553f2670b2d285a96cf09cd472)) - fix(desktop): make lint and unit-test gates work on Windows ([#2943](https://github.com/block/buzz/pull/2943)) ([`545bb46b8`](https://github.com/block/buzz/commit/545bb46b824a3fbf4401062f03b72531d832ebb9)) - feat(desktop): add search to agent emoji picker ([#2630](https://github.com/block/buzz/pull/2630)) ([`313f793c8`](https://github.com/block/buzz/commit/313f793c8753d413c22ff8edfe420d5ee78708bc)) - fix(desktop): keep identity key help dialog readable in dark mode ([#2854](https://github.com/block/buzz/pull/2854)) ([`be275cfc6`](https://github.com/block/buzz/commit/be275cfc6c7b80fe43e9d66c6d14b6d2bbe58a10)) - feat(acp): title agent sessions from the agent and channel name ([#3028](https://github.com/block/buzz/pull/3028)) ([`f2fe3b63c`](https://github.com/block/buzz/commit/f2fe3b63c21be55907175715c076cd3a9195b74d)) - feat(git): use agent display name as git author name ([#3040](https://github.com/block/buzz/pull/3040)) ([`18eef633d`](https://github.com/block/buzz/commit/18eef633d88ac465c61d98f12655fbf51dc3ca44)) - fix(deps): bump nostr to 0.44.6 for RUSTSEC-2026-0216 (NIP-44 remote DoS) ([#3135](https://github.com/block/buzz/pull/3135)) ([`31e2de196`](https://github.com/block/buzz/commit/31e2de1966672e73e026af3c54f3a1a9a2f5e103)) - fix(desktop): read the newest pair-scoped harness log ([#3134](https://github.com/block/buzz/pull/3134)) ([`654f38490`](https://github.com/block/buzz/commit/654f384906b5c720a60a199d85031a6f1cb6efc9)) - feat(desktop): handle project work from Inbox ([#3117](https://github.com/block/buzz/pull/3117)) ([`c5c4f390b`](https://github.com/block/buzz/commit/c5c4f390b6713256e2efb8394c59823ebad73db6)) - fix(desktop): clarify identity key button when key exists ([#2357](https://github.com/block/buzz/pull/2357)) ([`87b3fcd3c`](https://github.com/block/buzz/commit/87b3fcd3c0131683569dd4268b099d18b25dcd5e)) - Restore Goose and Buzz Agent to onboarding harness selection ([#2731](https://github.com/block/buzz/pull/2731)) ([`7fc0cc82d`](https://github.com/block/buzz/commit/7fc0cc82db4d9dced9c258bbe8b530164a832a77)) - fix(desktop): render rich project work item content ([#3100](https://github.com/block/buzz/pull/3100)) ([`afb272bb7`](https://github.com/block/buzz/commit/afb272bb7b8d7d45d7de676fa97dcd5a8eefacc7)) - feat(acp): bring your own harness (BYOH) — generic ACP runtime seam + settings gallery ([#2773](https://github.com/block/buzz/pull/2773)) ([`95fdf9788`](https://github.com/block/buzz/commit/95fdf978800982389b120c66ff5e766d785419c7)) - feat(desktop): use collective mesh routing for Auto ([#2825](https://github.com/block/buzz/pull/2825)) ([`16d4ec335`](https://github.com/block/buzz/commit/16d4ec335e210295a9d9f77f36c1e85a18b6814a)) - fix(desktop): strip legacy baked team instructions from stored prompts ([#3035](https://github.com/block/buzz/pull/3035)) ([`aee631448`](https://github.com/block/buzz/commit/aee63144843854ee32ed9d36a2e7511c82ddc6b0)) - feat(agents): lower default agent parallelism from 24 to 10 ([#3038](https://github.com/block/buzz/pull/3038)) ([`5d8ede446`](https://github.com/block/buzz/commit/5d8ede446f8fdc48146fe56d389cab6bf3500f92)) - Polish community rail and mobile pairing ([#2972](https://github.com/block/buzz/pull/2972)) ([`e6c90bb7c`](https://github.com/block/buzz/commit/e6c90bb7c430d1b2af16508b634f9a5283b7fa3b)) - fix(desktop): remove bundled libsystemd from AppImage ([#2353](https://github.com/block/buzz/pull/2353)) ([`a31fc4d2f`](https://github.com/block/buzz/commit/a31fc4d2f35d51cdf45ff8c61fc3a07f49c665e8)) - fix(desktop): make agent definition authoritative for model/provider/prompt ([#1968](https://github.com/block/buzz/pull/1968)) ([`8c0e8cb16`](https://github.com/block/buzz/commit/8c0e8cb1656b04ad269bce3c2deeda2a943ae78a)) - chore(desktop): delete dead persona catalog UI cluster ([#2886](https://github.com/block/buzz/pull/2886)) ([`8e67cf399`](https://github.com/block/buzz/commit/8e67cf399d0291bcdbc69cd0402983ca030f05bb)) - fix(desktop): surface install failures hidden by curl-pipe exit codes ([#2892](https://github.com/block/buzz/pull/2892)) ([`166c6655e`](https://github.com/block/buzz/commit/166c6655e8bca87d83ad60c087fb70a32a026baf)) - Refactor managed-agent runtime into cohesive modules ([#2974](https://github.com/block/buzz/pull/2974)) ([`74b63e184`](https://github.com/block/buzz/commit/74b63e1846212af6e6751a62cfc631f74b1dfe07)) - fix(desktop): make Linux AppImage GStreamer work on non-Debian distros ([#2176](https://github.com/block/buzz/pull/2176)) ([`cc6c4d347`](https://github.com/block/buzz/commit/cc6c4d3471629fad018bcf645f9471a01b9ffe2f)) - refactor(desktop): remove Agent directory section from Agents page ([#2290](https://github.com/block/buzz/pull/2290)) ([`5d1233e84`](https://github.com/block/buzz/commit/5d1233e841b0efa91470bb45467b2c8e4284ebf6)) - fix(desktop): enable arboard Wayland backend so Linux copies reach the Wayland clipboard ([#2904](https://github.com/block/buzz/pull/2904)) ([`ab7aa8b12`](https://github.com/block/buzz/commit/ab7aa8b1200710dbc2d7a8661ed5aab95c4199c1)) - fix(desktop): supervise and re-arm relay-mesh runtime ([#2823](https://github.com/block/buzz/pull/2823)) ([`aa51dab9d`](https://github.com/block/buzz/commit/aa51dab9da5fef7054d03cf1a1207986d0000684)) - fix(agents): run live Databricks discovery instead of the fallback list ([#2890](https://github.com/block/buzz/pull/2890)) ([`8eb6e3eb6`](https://github.com/block/buzz/commit/8eb6e3eb601174249642373a6a367262fa476753)) - fix(desktop): retire prepend mode on every reader wheel ([#2913](https://github.com/block/buzz/pull/2913)) ([`07d0265cf`](https://github.com/block/buzz/commit/07d0265cfc212ef02e1c26153bf58ff46ce5ffe6)) - fix(desktop): consolidate prepend scroll correction ([#2855](https://github.com/block/buzz/pull/2855)) ([`25e7864b3`](https://github.com/block/buzz/commit/25e7864b35f4dfd1c0ff31304a38555230a85f8d)) - fix(desktop): track concurrent agent turns up to the harness maximum ([#2882](https://github.com/block/buzz/pull/2882)) ([`20bff5910`](https://github.com/block/buzz/commit/20bff591023daffc5ee1032cff02b54b75da3567)) - fix(relay): preserve reconnect backoff ([#2759](https://github.com/block/buzz/pull/2759)) ([`499c5d349`](https://github.com/block/buzz/commit/499c5d349dab13bc906b1af5fe1fcb09ce2afa81)) - refactor(relay): expose reconnect timing policy ([#2310](https://github.com/block/buzz/pull/2310)) ([`2f0041595`](https://github.com/block/buzz/commit/2f0041595d72529c06885680d2bd07ddb6a0beb4)) - fix(desktop): clear stale working badges on agent stop/restart ([#2803](https://github.com/block/buzz/pull/2803)) ([`a64cc71f6`](https://github.com/block/buzz/commit/a64cc71f6c1605279b1a6fbd0fe904a2984cbdb0)) - fix(desktop): surface agent rename relay profile sync failure as a warning toast ([#2279](https://github.com/block/buzz/pull/2279)) ([`5e3d2e484`](https://github.com/block/buzz/commit/5e3d2e4849c0f2512330801d804fb96f4ab72d28)) - fix(discovery): inject PATH into Codex adapter planning ([#2767](https://github.com/block/buzz/pull/2767)) ([`6ab3835f3`](https://github.com/block/buzz/commit/6ab3835f3fe89ee215819fe8d193463c0ae7472b)) **To release:** merge this PR. The tag and build will happen automatically. Signed-off-by: Wes <wesbillman@users.noreply.github.com> |
||
|
|
be13b4bb9c |
fix(desktop): probe legacy Goose install dir on Windows (#3248)
Goose's pre-[#2680](https://github.com/block/buzz/pull/2680) Windows installer unpacked the CLI to `%USERPROFILE%\goose\goose.exe`. That directory is on no standard `PATH`, and `common_binary_paths()` never probed it, so users who installed Goose with the legacy installer stayed permanently undiscovered — the residual half of #2239. `resolve_command_uncached` finds binaries outside `PATH` only by scanning `common_binary_paths()`, so adding the directory there is the whole fix: Windows basename expansion already supplies `goose.exe`/`.cmd`/`.bat`, and discovery, readiness probes, and spawn all route through the same shared resolver. No Goose-specific resolution path is introduced. The entry sits beside the existing Codex `%LOCALAPPDATA%\Programs\OpenAI\Codex\bin` probe in the same `#[cfg(windows)]` block. The regression test is `#[cfg(windows)]` and is CI-reachable, not dead code — the `desktop-build-windows` job runs `cargo test --manifest-path desktop/src-tauri/Cargo.toml --target $env:TARGET` on `windows-latest`. It asserts the probe list rather than planting a binary: `common_binary_paths` is a process-lifetime `OnceLock`, so a test cannot deterministically re-seed `USERPROFILE`, and planting an executable under the real user profile is not an acceptable side effect. Verified locally by widening the `cfg` to build on macOS — the test passes with the probe and fails without it. The `check-file-sizes.mjs` override for `managed_agents/discovery.rs` moves 1835 → 1841, the exact post-`cargo fmt` gate count. Verified both directions: 1841 passes, 1840 fails. Signed-off-by: Will Pfleger <pfleger.will@gmail.com> |
||
|
|
94675e0d25 |
refactor(desktop): extract install command execution into install_exec (#3251)
`commands/agent_discovery.rs` was pinned at its 2167-line file-size ceiling with zero headroom, blocking the install-supervision and install-log work queued behind it. Install command *execution* is a clean seam and moves into `commands/agent_discovery/install_exec.rs` together with its tests, matching the existing `managed_node.rs` / `post_install_verification.rs` split under the same module. Moved: `INSTALL_MAX_ATTEMPTS`, `run_install_command_with_retry`, `run_install_with_retry`, `install_failure_is_retryable`, `install_retry_backoff`, `annotate_retry_attempts`, `run_install_command`, `truncate_output`, `floor_char_boundary`, and the install-retry test block. Command *construction* (`install_shell_command`, `install_powershell_command`, `build_install_command`) stays in the parent — the new module owns only what happens once a `Command` exists. Public surface is exactly one `pub(super) fn run_install_command_with_retry`. The extraction is behavior-preserving, verified by diffing the moved text against the original line ranges: the parent is original-minus-cuts plus the intended edits, and the moved code is byte-identical except for the `pub(super)` marker, the `build_install_command` → `prepare_install_command` call site, and the new function described below. Two parent imports (`std::io::Read`, `InstallStepResult`) became unused and were dropped. ### Install working directory (#2245) Absorbed from #3090. A packaged desktop launch inherits `/` as its working directory, so installers that write relative to the CWD fail on a read-only root. The new `prepare_install_command` builds the command and applies `default_agent_workdir()`, and it is the only builder `run_install_command` calls — so no spawn path can bypass the workdir. This differs from #3090 in the test: that version spawned `pwd` through the real install shell and deleted `test_install_shell_command_returns_ok_on_unix` to make room. Here the prepared `Command` is asserted directly via `get_current_dir()` — hermetic, no shell spawn — and the existing test is kept. ### Tests Four new, on top of the moved retry block: - `test_prepared_install_command_uses_default_workdir` — every install child carries `default_agent_workdir()`. - `test_truncate_output_leaves_short_output_untouched` — under the cap, byte-for-byte passthrough. - `test_truncate_output_keeps_head_and_tail_with_marker` — over the cap, both ends survive and the marker names the omitted byte count. - `test_truncate_output_does_not_split_multibyte_characters` — the boundary floor prevents a mid-codepoint cut. `truncate_output` had no coverage anywhere before this. ### File-size gate `check-file-sizes.mjs` override for `agent_discovery.rs` moves 2167 → 1808, the exact post-`cargo fmt` gate count — verified both directions (1808 passes, 1807 fails). `install_exec.rs` is 458 lines and needs no override; the default 1000-line limit covers it. Related: #3090, #2245 Signed-off-by: Will Pfleger <pfleger.will@gmail.com> |
||
|
|
d8f9d87c17 |
Polish composer activity layout and transitions (#3151)
## Summary - Replace the permanently reserved composer activity row with a conditional, content-driven accessory for channel and thread composers. - Keep the composer dock geometrically stable while activity appears, so the composer’s bottom edge resizes smoothly without shifting the conversation. - Preserve translucent backdrop blur during the resize with a stable dock-level blur layer, and keep thread overlay/focus-mode alignment consistent. - Keep bottom-pinned virtualized conversations fully visible as composer content, zoom, or viewport height changes without repinning readers who have scrolled into history. - Refine the activity lockup with aligned avatars/text and a subtle, reduced-motion-safe shimmer. - Centralize the dock’s quiet inset, activity rail, released space, and activity offset in one CSS-variable geometry contract. This takes a different, systemic route from the spacing reduction proposed in #2602 and supersedes that approach. ### Related issue Related PR: #2602 ### Testing - `./scripts/check-branch-skew.sh` - `just desktop-check` - `just desktop-test` — 3,638 passing - `useAnchoredScroll.test.mjs` — virtualized viewport resize follows the explicit bottom state - Focused desktop smoke E2E — 6 passing across stable dock geometry, multiline growth, viewport resize, reduced motion, blur ownership, and thread overlay alignment - Pre-push hooks passed for organization safety, branch skew, desktop checks/tests, Rust tests, workspace tests, and desktop Tauri tests - Mobile pre-push is independently red on latest `main`; the exact `activity_page_test.dart` compiler failure reproduces on untouched `origin/main` - Visually tested channel and thread composers across quiet/activity states, multiline composer growth, and thread overlay/focus mode https://github.com/user-attachments/assets/ed0b08d9-18e0-4061-b272-ab509dbcd8ee --------- Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com> |
||
|
|
d500c2d5cf |
feat(invites): add use-limited invite links (#3141)
## Summary - add database-backed v2 invite links with optional maximum-use limits and atomic final-slot redemption - preserve v1 invite compatibility while adding exhausted/expired/invalid client handling across desktop, web, and mobile - emit structured claim-outcome logs with community, invite ID, outcome, maximum uses, and post-claim count ## Verification - `cargo fmt --all -- --check` - `cargo test -p buzz-db` (85 passed, 134 Postgres-dependent ignored) - `cargo clippy -p buzz-db --all-targets -- -D warnings` - desktop `npm run typecheck` - push hook: desktop checks/tests, desktop Tauri tests, Rust tests, and branch-skew passed - Postgres integration tests were previously reviewed green at the pre-rebase tree; local rerun on this session was unavailable because Postgres/Docker were not running - mobile push-hook check could not start because Flutter is unavailable locally --------- Signed-off-by: Kalvin Chau <kalvin@block.xyz> Signed-off-by: npub1dccv64krpcpse5cmkzfeh998cftungyatw3djt8jwdw6g43f7fyqzzmrf7 <6e30cd56c30e030cd31bb0939b94a7c257c9a09d5ba2d92cf2735da45629f248@buzz.block.builderlab.xyz> Co-authored-by: npub1c4alndp82zyt9veaklm5d965quss79vlhk9awv7qu5erwhmf42qqlvc25c <c57bf9b4275088b2b33db7f746975407210f159fbd8bd733c0e532375f69aa80@buzz.block.builderlab.xyz> Co-authored-by: npub122y0pqkertljmedu303rl0aqrj3w8pvu43t6jxm6875lzg6f2pwqegc3xc <5288f082d91aff2de5bc8be23fbfa01ca2e3859cac57a91b7a3fa9f12349505c@buzz.block.builderlab.xyz> Co-authored-by: npub1dccv64krpcpse5cmkzfeh998cftungyatw3djt8jwdw6g43f7fyqzzmrf7 <6e30cd56c30e030cd31bb0939b94a7c257c9a09d5ba2d92cf2735da45629f248@buzz.block.builderlab.xyz> |
||
|
|
98a7b13348 |
fix(node): bump Buzz-supplied Node runtimes past OpenClaw's >=24.15.0 floor (#3218)
## Problem User report: ``` Buzz Node mismatch: Buzz supplies Node 24.14.0; OpenClaw requires >=24.15.0. All 10 ACP workers immediately crash. ``` Buzz supplies Node to agent processes from two places, and both were below OpenClaw's floor: | Supply path | Was | Now | |---|---|---| | hermit dev env (`bin/.node-*.pkg`) — the 24.14.0 in the report | 24.14.0 | **24.15.0** (newest hermit publishes; satisfies `>=24.15.0`) | | Desktop managed runtime (`managed_node.rs` / `managed_node_paths.rs`) | v24.11.0 | **v24.18.0** (current latest v24) | The managed runtime sits **first** on the worker PATH (`managed_agents/runtime/path.rs`), so a user-installed newer Node can't mask a stale managed one — the pin itself has to move. ## Verification - SHA-256 digests for all six platform artifacts taken from `https://nodejs.org/dist/v24.18.0/SHASUMS256.txt`; darwin-arm64 independently re-verified by downloading the tarball (hash match), extracting, and running `bin/node --version` → `v24.18.0`. - All artifacts within `MANAGED_NODE_MAX_BYTES` (largest linux-x64 at 57 MB < 90 MB cap); tar.gz layout keeps the `node-vX-platform/bin/node` shape `verify_node_tree` expects. - `cargo test --lib` in `desktop/src-tauri`: **1801 passed, 0 failed** at this commit; `cargo fmt --check` + `cargo clippy --lib -D warnings` clean. - Existing readiness check (`node --version == MANAGED_NODE_VERSION`) makes upgrade automatic: installed v24.11.0 trees fail readiness and the installer stages v24.18.0 atomically (rename with `.old` rollback — existing logic, unchanged). Note: CI `node-version: 24.14.1` pins in `release.yml`/`windows-canary.yml` are build-env only (already `>=` nothing OpenClaw touches) and left alone to keep this minimal. Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> |
||
|
|
9810d85459 |
fix(desktop): preserve thread anchor through layout reflow (#3212)
## Summary - keep a thread presentation-switch anchor pinned while focus/split width reflow settles - retire the temporary anchor only after resize correction and a following paint confirm the row is visible - preserve the existing external-target resolution behavior and viewport E2E contract ## Root cause The focus and split wrappers intentionally retain the same thread surface, but switching wrappers also changes the message column width. `useAnchoredScroll` centered the captured message once and immediately cleared the one-shot layout target. A later text reflow could then move that message outside the viewport with no remaining target to correct it. ## Verification - `pnpm check` - `pnpm typecheck` - `pnpm test` — 3,699 passed - `pnpm build:e2e` - `pnpm exec playwright test tests/e2e/thread-focus-mode.spec.ts --project=smoke --repeat-each=10` — 20 passed - push hook: branch-skew, Desktop check, and Desktop full unit suite passed --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
cb2a265b53 |
feat(search): parse from:/in:/after:/before: and pass them in the filter (#2871)
## Summary - Adds a pure-TS operator parser (`from:` / `in:` / `after:` / `before:`) with unit tests. Invalid date tokens stay in the FTS text. - Extends `search_messages` so the desktop can send `authors` / `since` / `until` (and existing `#h`) on the filter the relay already understands. - Wires topbar search to strip operators from the prefix query, resolve `in:` against local channels and `from:` against hex pubkeys / known agents, then pass the structured fields through. This is part 1 of #2853 (parser + command plumbing). Autocomplete chips / richer `from:@name` resolution can follow in a second PR. ## Test plan - [x] `node --import ./test-loader.mjs --experimental-strip-types --test src/features/search/lib/parseSearchOperators.test.mjs` - [x] Added `search_messages_filter_emits_operator_fields` unit test (full `buzz-desktop` crate build needs local sidecar binaries in this environment) - [ ] Manual: topbar `deploy from:<hex> after:2024-01-01` emits authors/since on the bridge filter and returns narrowed hits Made with [Cursor](https://cursor.com) --------- Signed-off-by: Jatinder Mahajan <jatinder.mahajan@certifyos.com> Co-authored-by: Jatinder Mahajan <jatinder.mahajan@certifyos.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Will Pfleger <pfleger.will@gmail.com> |
||
|
|
0019f80765 |
fix(desktop): fetch join policies through native networking (#2862)
## Context Adding an existing community by relay URL could fail with `Community rejected: Load failed` even when its WebSocket endpoint was reachable. The Add Community flow fetched `/api/join-policy` from the WebView, so a relay without a matching CORS allowance blocked the policy request before the app could join it. ## Summary This bug fix fetches join policies through Tauri's native networking layer for direct URL joins. Invite-code discovery, policy acceptance, and signed invite claims remain on the WebView path so those operations can migrate together later. ## Changes - Uses native networking for Add Community and first-community direct URL join-policy requests. - Validates relay schemes, rejects URLs containing credentials, and refuses redirects. - Bounds declared and chunked native responses before JSON parsing. - Preserves existing `404`, non-success status, malformed JSON, and absent-policy behavior. - Requires every join-policy caller to choose its transport explicitly. Public relays using Buzz's default permissive CORS configuration are not known to be affected. ### Related issue Related to #2872. ### Testing #### Reviewer-reproducible examples End-to-end red/green requires a relay with restrictive CORS and a Buzz identity authorized to join it. ##### Red: `main` From a clean checkout of `main`: ```bash . ./bin/activate-hermit just staging ``` In Buzz Desktop: 1. Add another community so the restrictive-CORS relay can be removed. 2. Remove that relay. 3. Open Add Community and enter the relay's WebSocket URL. 4. Select Add Community. Observed result: ```text Community rejected: Load failed ``` ##### Green: this PR From a clean checkout of this branch: ```bash . ./bin/activate-hermit just staging ``` Repeat the same steps above. Observed result: ```text The community rejoins successfully. ``` Supporting checks: - Six native join-policy tests, including oversized declared and chunked responses. - Four TypeScript API tests, including the native command contract. - E2E build and four focused onboarding and sidebar Playwright tests. - Full `just ci` and pre-push suites. - Builderbot, Kalvin, and minimize-diff review fanout found no actionable issues after the final rebase. |
||
|
|
7ca0bbd946 |
fix(desktop): republish agent identity records when a persona rename propagates (#2607)
## Problem Part of #2423 (renaming personal agents desynchronises identity). Renaming an agent definition (persona) propagates the new display name to its linked agent instances (`propagate_persona_name_rename` in `desktop/src-tauri/src/commands/personas/mod.rs`) and saves `managed-agents.json` — but, unlike the instance-rename path (`update_managed_agent`), it never re-retains the renamed instances' kind:30177 managed-agent identity records. `record.name` is part of the published identity projection (`agent_event_content`), so after a persona rename: - `managed-agents.json` says the NEW name, - the retained kind:30177 row (retention.db → relay flush loop) still carries the OLD name, with the OLD `created_at`. The stale identity record stays live on the relay until the next app launch, when the boot-time reconcile (`reconcile_agents_in_dir`) finally notices the content diff and republishes. Until that restart, any surface that resolves agents from kind:30177 records (second desktop of the same owner, CLI, other NIP-AP clients) sees the OLD name bound to the agent pubkey while the kind:0 profile already shows the NEW one — the name→identity binding desync described in #2423, and consistent with the report's observation that repairing state required "a separate restart". ## Fix - Extract the per-record retain body of the boot reconcile into `managed_agents::reconcile::retain_agent_record(conn, keys, record) -> Result<bool, String>` — one shared content-diff + monotonic-`created_at`-bump engine (returns whether a row was rewritten). `reconcile_agents_in_dir` now calls it per record (behavior unchanged; existing reconcile tests still pass). - `commands::agents::retain_managed_agent_pending` delegates to the shared engine instead of carrying a duplicate implementation (same semantics: projection-equality no-op guard, monotonic bump, `pending_sync = 1`). - `update_persona` (Phase 1, still under the store lock, after `save_managed_agents`): call `retain_managed_agent_pending` for every record the rename propagated to — mirroring `update_managed_agent`. Avatar-only edits are deliberately excluded (the avatar is not part of the kind:30177 projection; retaining would be a guaranteed no-op). No new events, kinds, or APIs — this uses the existing signed-event retention and flush pipeline, per CONTRIBUTING's guidance to prefer a signed Nostr event and the existing ingest path over endpoint-specific JSON APIs. ## Out of scope (deliberately) - Rename → runtime restart is #1823, fixed by open PR #2507 (spawn_hash). - Surfacing kind:0 relay profile-sync failures on rename is PR #2302/#2279 territory (and largely superseded by the merged rollback in #2258). - Mention-picker UX (owner/status disambiguation) and channel-membership repair for stale identities: TS-side, noted in #2423, not touched here. ## Test evidence Two new unit tests in `desktop/src-tauri/src/managed_agents/reconcile/tests.rs` (same harness as the existing reconcile tests — tempdir + retention.db + fresh keys, no AppHandle): - `rename_re_retains_identity_record_with_new_name` — retain "Fizz", confirm flush, rename to "Spark", re-retain: row keeps the pubkey coordinate, carries the new name only, is `pending_sync`, and its `created_at` is strictly past the retained head (replaceable-event acceptance). - `retain_agent_record_is_noop_when_unchanged` — an unchanged projection does not rewrite the row and produces zero `pending_sync` churn. Ran scoped per CONTRIBUTING build discipline (from `desktop/src-tauri`): ``` cargo fmt -p buzz-desktop # applied, clean cargo clippy -p buzz-desktop --lib --tests -- -D warnings # exit 0, no warnings cargo test -p buzz-desktop --lib # full lib suite ``` Full `buzz-desktop` lib suite: **1562 passed, 0 failed, 13 ignored** — including all 12 `managed_agents::reconcile` tests (10 pre-existing, all unmodified in behavior, plus the 2 new regression tests above). ## Links - Issue: https://github.com/block/buzz/issues/2423 - Adjacent (no overlap): PR #2507 (rename-restart, #1823), PRs #2302/#2279 (kind:0 sync-failure surfacing), merged #2258 (instance-rename rollback). --------- Signed-off-by: Sean Gearin <sgearin@gmail.com> Co-authored-by: Sean Gearin <sgearin@gmail.com> Co-authored-by: Will Pfleger <pfleger.will@gmail.com> |
||
|
|
de13960505 |
fix(desktop): keep project Inbox previews compact (#3193)
## Summary Project pull request and issue previews in Inbox no longer expand when their content begins with a Markdown heading. Heading-form titles now match neighboring Inbox typography while preserving the existing two-line truncation. ### Related issue Related: #3117 ### Testing - `pnpm build:e2e` - `pnpm exec playwright test tests/e2e/project-inbox.spec.ts --project=smoke --retries=0` — passed - `pnpm exec biome check tests/e2e/project-inbox.spec.ts src/shared/styles/globals/markdown.css` — passed - `pnpm check:px-text` — passed - Captured `desktop/test-results/inbox-preview/01-project-preview.png` The repository-wide desktop file-size gate remains blocked by the pre-existing 1,026-line `AgentCreationPreview.tsx` on `main`; the change itself introduces no file-size regression. Signed-off-by: Thomas Petersen <thomasp@squareup.com> |
||
|
|
2bd4c24b71 |
Inbox refactor (#2045)
## Why The Inbox mixed overlapping feed categories with personal work queues, so **All** was not actually comprehensive and several filters did not make it clear why an item appeared. Threads and DMs could produce one row per event instead of one row per conversation, drafts were hidden until selected, and reminders appeared through multiple competing presentations. This refactor makes the **Inbox** a focused, conversation-oriented place to catch up on work relevant to you. It is intentionally not a mirror of every unread event in every channel. ## What changed - Keep the destination named **Inbox** and use the standard Lucide bell icon. - Refocus **All** on DMs, mentions, thread replies, needs-action items, replies from agents the user owns or controls, due reminders, and active drafts. - Exclude generic top-level channel traffic and updates from agents the user does not own or control. - Group each thread or DM into one row, sorted by latest activity. - Resume an unread conversation at its oldest unread message while opening the full thread or DM in the detail pane. - Reuse the existing **New** divider at the unread boundary. - Make the detail title a direct link to the canonical conversation. - Give Reminders and Drafts the same list/detail interaction and location metadata as conversation rows. - Separate Reminders and Drafts from message filters with a subtle divider, without adding another labeled section. - Put reminder and draft counts beside their corresponding filter labels instead of on the generic filter button. - Preserve the selected conversation when switching filters if it remains valid; otherwise select a valid replacement without flashing stale detail. - Use filter-specific empty states and rename the options toggle to **Show unread only**. - Ship the focused behavior directly. The earlier experiment gate, Custom view, and default-view controls have been removed from this PR to keep the first pass focused. ## Filter model | Filter | What appears | | --- | --- | | **All** | One row per personally relevant conversation, plus due reminders and active drafts. Includes DMs, mentions, thread replies, explicit needs-action items, and replies from agents the current user owns or controls. Excludes generic top-level channel traffic, other agents' updates, and reminders that are not due yet. | | **Mentions** | Conversations containing a direct mention. Each conversation appears once and opens with full context. | | **Threads** | Conventional threaded replies, grouped to one row per thread. Broadcast replies are not treated as conventional thread replies. | | **Needs action** | Feed items explicitly classified as requiring action. | | **Agents** | Conversations whose representative response was authored by an agent the current user owns or controls, including top-level DM responses. If a human replies afterward, the conversation leaves this filter until an owned agent responds again. | | **Reminders** | All pending reminders, including upcoming reminders that stay out of **All** until they are due. | | **Drafts** | Active drafts, ordered by their last real edit time. | ## Grouping, ordering, and state - A thread or DM creates one Inbox row rather than one row per event. - An unread conversation resumes at its oldest unread message so intervening context is not skipped. - Conversation rows still sort by their latest activity. - The detail pane opens the full available conversation and shows the shared **New** divider before the first unread message. - Upcoming reminders appear only in **Reminders**. - When a reminder becomes due, it enters **All** at its trigger time. If its source conversation is already represented, the reminder state merges into that row instead of creating a duplicate; otherwise it appears as a standalone reminder row. - A due reminder can enrich a row in another relative filter when that conversation already qualifies for the filter. Reminder lifecycle remains separate from message read state. - Drafts appear in **All** by their last real edit time. Opening an unchanged draft does not move it to the top. - Reminder and draft rows show their location as `In #channel` or `In DM with <name>`. - **Show unread only** hides reminder and draft work queues because they do not share message unread semantics. ## Removed or narrowed - **Remove the old Activity filter.** It overlapped with All while still omitting items All now includes. - **Narrow Agents.** It no longer gathers every agent participating in a shared thread or subsequent human follow-ups. - **Remove duplicate reminder presentations.** The aggregate pending-reminders jump and duplicate generic feed rows are replaced by one list/detail model. - **Remove Custom and default-view settings from this pass.** They added considerable state and UI before the core model had been validated. - **Do not add section labels for Reminders and Drafts.** A divider communicates the distinction without creating another hierarchy in the menu. ## Risk assessment Medium implementation risk because this changes composition, grouping, ordering, read behavior, and personal queues in a primary desktop view. The implementation is scoped to the desktop UI and its local feed projection; it does not change relay schemas or public APIs. ## Testing - Desktop formatting, lint, file-size, text-size, and TypeScript checks passed. - Desktop unit suite: **3,663 passed, 0 failed**. - Desktop E2E production build passed. - Playwright smoke coverage across every spec touching this surface (`channels`, `smoke`, `profile`, `project-inbox`, `community-rail`, `integration`, `drafts-screenshots`): **118 passed, 0 failed**. - Full Playwright smoke project: **732 passed, 1 skipped**. Three local failures were investigated and cleared — `community-rail` keyboard reorder passed on re-run (flaky), while `relay-reconnect:97` and `video-attachment:223` are untouched by this commit (the only change to shared `tests/helpers/bridge.ts` is a comment) and pass in CI. - Unit coverage includes focused All matching, owned-agent filtering, conversation grouping, oldest-unread selection, selection stability, chronological reminder/draft composition, trigger-time reminder ordering, and duplicate reminder suppression. ## Update: July 27, 2026 The naming decision is settled: the surface stays **Inbox**. An earlier pass in this branch had renamed it to **Activity**; that rename has been reverted in `9c00d2d6e`, which is naming-only and changes no behavior. The revert covers file names, component/hook/type/constant identifiers, the sidebar label and tooltip, the `Inbox options` and `Filter inbox:` aria-labels, and the corresponding test names, test ids, and fixture ids. Three things were deliberately left as `activity`: - **The feed API contract** — the `activity` / `agent_activity` categories, the `feed.activity` and `feed.agentActivity` keys, and the `types=` query parameter. These are the server's names, not the surface's. - **Plain-noun usage** — empty states such as "No activity yet", plus `latestActivityAt` and `PROJECT_ACTIVITY_KINDS`. - **Pre-existing agent, project, and profile activity code**, which refers to a different concept entirely. The earlier experiment-gate approach has also been dropped, so `tests/helpers/bridge.ts` no longer claims that an Activity preview feature exists — `preview-features.json` has no such entry and the seed helper enables every desktop feature. Generated with Codex --------- Signed-off-by: Clay Delk <clay.delk@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
99da5b7ebb |
Fix composer selection formatting and drop overlay (#3172)
## Summary - scope code blocks and list formatting to the selected composer text - use the Buzz primary color for the selection formatter - extend the channel drop overlay over the composer with matching corners, blur, and accessible contrast across themes ## Validation - `just ci` - composer selection formatting E2E tests - file attachment and all-theme drop contrast E2E tests --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
75588eaff2 |
Refine pending message status (#3153)
## Summary - Replace the all-caps, widely tracked pending-message label with sentence-case `Sending` - Match the surrounding timestamp and metadata spacing ## Why The status briefly appeared as `SENDING`, unlike nearby message metadata. ## Validation - Desktop typecheck - Focused Biome and text guards - 3,637 desktop unit tests Signed-off-by: kenny lopez <klopez4212@gmail.com> |
||
|
|
174c38e4bd |
fix(desktop): recover full local storage on startup (#3182)
## Summary - recover already-full Buzz installs before desktop initialization without deleting healthy caches - enforce a global 2 MiB UTF-16 byte budget across disposable message, channel, timeline-skeleton, and sidebar-skeleton caches, regardless of relay count - route all disposable cache writes through quota recovery and reserve roughly 3 MiB of WebKit's observed ~5 MiB quota for durable state - preserve communities, identities, preferences, drafts, and read state; match only delimiter-qualified disposable namespaces ## Context WebKit enforces an approximately 5 MiB per-origin localStorage quota and Tauri does not expose an app-level knob to raise it to 50 MiB. Buzz 0.4.26 shipped reactive recovery for selected durable writes, but disposable writers swallowed quota failures and their existing limits were count-based per relay rather than byte-based per origin. This PR handles both halves: upgrade recovery for already-wedged origins and proactive global headroom so disposable snapshots cannot drive the origin back to the cliff. ## Safety - startup first probes a one-byte marker; healthy installs retain their caches - only if the marker write fails are the four relay-rehydratable cache namespaces removed - namespace matching requires the `v1:` delimiter, preventing future `v10` or similarly named durable keys from matching - oversized individual snapshots are rejected; crossing the global budget evicts disposable snapshots only - failed recovery leaves the marker absent, so the next launch retries ## Verification - `pnpm test` — 3,670 passed - `pnpm check` - `pnpm typecheck` - push hooks: branch-skew, desktop-check, desktop-test passed - byte-budget tests cover UTF-16 accounting, multiple relays, oversized writes, durable-state preservation, healthy startup, full startup, marker retry, and namespace near misses --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
4d8b676bb2 |
fix(desktop): keep collapsed table separators out of spoilers (#3169)
## Summary - preserve collapsed GFM table separator rows as literal text - prevent `||---:|---|---||` from becoming an animated spoiler canvas - leave normal spoilers and valid multiline tables unchanged ## Root cause When table newlines are lost, adjacent row pipes become `||`. The spoiler remark plugin interpreted the delimiter row between those pairs as a hidden spoiler, so the reported "static" was the spoiler particle animation rather than table layout churn. ## Safety The guard only applies to a paragraph span made entirely of text that exactly matches a multi-column GFM delimiter row. The narrow syntax collision is that an intentional spoiler containing only a delimiter row such as `||---|---||` now renders literally. ## Verification - desktop pre-push checks passed (3,662 tests) - desktop TypeScript typecheck passed - independent review found no blocking issues Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
d98da7389e |
feat(desktop): redesign agent runtime settings (#3093)
**Category:** improvement **User Impact:** Users can understand, install, authenticate, and manage agent runtimes from one progressively disclosed Agents settings experience. **Problem:** Runtime health and custom harness management were split across overlapping settings surfaces, exposing low-level configuration too early while leaving setup and authentication states hard to understand. **Solution:** Consolidate those operations into one stable runtime list and an Add runtimes catalog, with task-oriented state labels, direct setup actions, and custom configuration contained in a dedicated form. <details> <summary>File changes</summary> **desktop/playwright.config.ts** Registers the visual coverage needed for the redesigned runtime catalog. **desktop/public/harness-logos/CREDITS.md** Documents bundled runtime-mark provenance and the decision not to ship the withdrawn OpenAI mark. **desktop/public/runtime-icons/codex.png** Removes the obsolete Codex bitmap in favor of the neutral fallback. **desktop/public/runtime-icons/goose.svg** Removes the old Goose asset now replaced by the theme-adaptive mark. **desktop/src-tauri/src/managed_agents/discovery.rs** Aligns runtime discovery guidance with the new task-oriented setup language. **desktop/src-tauri/src/managed_agents/discovery/runtime_metadata.rs** Updates runtime metadata used by the redesigned settings states. **desktop/src/features/agents/ui/runtimeAvailabilityWarning.test.mjs** Updates availability-warning expectations for the revised runtime guidance. **desktop/src/features/onboarding/assets/harness-logos/chatgpt.png** Removes the redundant bitmap from the unified runtime icon pipeline. **desktop/src/features/onboarding/assets/harness-logos/goose.png** Removes the redundant Goose bitmap. **desktop/src/features/onboarding/ui/HarnessMarks.tsx** Adds theme-adaptive bundled runtime marks with safe fallbacks. **desktop/src/features/onboarding/ui/RuntimeIcon.tsx** Centralizes runtime logo rendering so settings and catalog rows cannot drift. **desktop/src/features/onboarding/ui/SetupStep.tsx** Aligns onboarding runtime setup copy with the settings terminology. **desktop/src/features/onboarding/ui/presetLogos.test.mjs** Guards bundled-logo behavior and prevents the withdrawn Codex mark from returning. **desktop/src/features/settings/ui/CustomHarnessForm.tsx** Reworks custom runtime creation and editing into a clear, dedicated catalog form. **desktop/src/features/settings/ui/HarnessCatalogDialog.tsx** Introduces the Add runtimes master-detail catalog, grouped setup states, loading treatment, and pinned actions. **desktop/src/features/settings/ui/HarnessManagementCard.tsx** Removes the superseded standalone custom-harness management surface. **desktop/src/features/settings/ui/HarnessRow.tsx** Provides stable operational runtime rows with install, update, authentication, edit, and delete behavior. **desktop/src/features/settings/ui/HarnessesSettingsPanel.tsx** Consolidates runtime health and custom management into one Agents settings panel. **desktop/src/features/settings/ui/SettingsPanels.tsx** Wires the consolidated panel into Agents settings. **desktop/src/features/settings/ui/harnessCatalogCopy.ts** Adds restrained, source-annotated runtime descriptions and setup guidance. **desktop/src/features/settings/ui/harnessCatalogLogic.test.mjs** Covers grouping, state labels, stable row order, actions, and adapter warnings. **desktop/src/features/settings/ui/harnessCatalogLogic.ts** Centralizes catalog grouping, actions, status labels, and runtime-safe warning copy. **desktop/src/features/settings/ui/harnessGalleryLogic.test.mjs** Removes obsolete gallery-only tests after consolidation. **desktop/src/features/settings/ui/harnessGalleryLogic.ts** Retains only the shared custom-runtime safety logic needed by the new surface. **desktop/src/shared/ui/config-nudge-attachment.tsx** Points configuration nudges to Agent runtimes with matching terminology. **desktop/src/testing/e2eBridge.ts** Adds deterministic runtime states for authentication and catalog E2E coverage. **desktop/tests/e2e/doctor-states.spec.ts** Verifies ready, setup-required, node-gated, authentication, loading, and error contracts. **desktop/tests/e2e/harness-catalog-screenshots.spec.ts** Captures whole-pane visual states for the runtime catalog experience. **desktop/tests/e2e/harness-management.spec.ts** Exercises catalog actions and custom runtime create, edit, authentication, and deletion flows. **desktop/tests/e2e/onboarding-agent-defaults.spec.ts** Updates default-runtime expectations for the consolidated experience. **desktop/tests/e2e/profile.spec.ts** Aligns profile navigation assertions with the new settings surface. </details> ## Reproduction steps 1. Open Settings → Agents and inspect Agent runtimes; ready, signed-out, installable, and setup-required runtimes should have stable rows and explicit actions. 2. Open Add runtimes and browse the Setup and Installed groups; select entries to see sourced guidance and a pinned Install or Setup guide action. 3. Select Custom harness, create a runtime, then edit and delete it; verify required-field gating and the blast-radius confirmation. 4. Exercise a signed-out runtime and connect it; the row should move from Sign-in needed to Ready without reordering. 5. Resize the window and switch themes to verify responsive layout and adaptive bundled marks. ## Screenshots | Mixed runtime states | Goose not installed | Sign-in needed | |---|---|---| | <img width="980" height="1000" alt="image" src="https://github.com/user-attachments/assets/28705b39-1d91-440a-a954-fb2d7d8ee759" /> | <img width="980" height="1000" alt="image" src="https://github.com/user-attachments/assets/e65ce8e1-8c85-4272-afb5-e6001b94d560" /> | <img width="980" height="1000" alt="image" src="https://github.com/user-attachments/assets/3987f355-960d-4744-8c26-b3746944a33c" /> | | Add runtimes catalog | Custom runtime | Setup guide | |---|---|---| | <img width="896" height="672" alt="image" src="https://github.com/user-attachments/assets/a2f32c8a-4dff-4929-af96-b07346933335" /> | <img width="896" height="672" alt="image" src="https://github.com/user-attachments/assets/b2e85a0c-3940-4875-a138-4035423c9de7" /> | <img width="896" height="672" alt="image" src="https://github.com/user-attachments/assets/5a690c1c-368e-4ee3-98b9-ffefdc3cf804" /> | Full 13-state screenshot matrix and review evidence: https://buzz.block.builderlab.xyz/channels/c5af9e3e-4317-4853-b3e3-ed9c15bc511d?event=ec88f6e472f2b08d6318ae76fd754fd4f218385f67cfe59b73034c4bc34c9252 --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz> Co-authored-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz> |
||
|
|
8995316844 |
fix(desktop): use forward slashes for git credential.helper on Windows (#3023)
## Problem On Windows, Projects **Remote** view shows an empty file tree and Sync/Clone fails with a mangled credential-helper path, for example: ``` C:\Users\<user>\AppData\Local\Buzz\git-credential-nostr.exe get: line 1: C:Users<user>AppDataLocalBuzzgit-credential-nostr.exe: command not found fatal: could not read Username for 'https://<relay>/git/...': terminal prompts disabled ``` Buzz injects an absolute path into `credential.helper` via `Path::display()`. On Windows that yields backslashes. Git for Windows runs credential helpers through MinGW bash, which treats `\` as escapes and destroys the path, so NIP-98 auth never runs and the blobless temp clone behind Remote view fails. macOS/Linux are unaffected (paths already use `/`). This is unrelated to shipping a stub helper - the bundled `git-credential-nostr.exe` is a real binary. User `~/.gitconfig` workarounds also cannot help here because Projects git sets `GIT_CONFIG_GLOBAL=/dev/null` and injects its own helper. Closes #3025 ## Fix Normalize the helper path to forward slashes before writing `GIT_CONFIG_VALUE_*`: - `desktop/src-tauri/src/commands/project_git_exec.rs` (Projects Remote / Sync) - `desktop/src-tauri/src/managed_agents/runtime.rs` (agent spawn git auth) Forward slashes are accepted by Git on every platform; on macOS/Linux the replace is a no-op. No `cfg(windows)`, packaging, or libgit2 changes. ## How to reproduce (before) 1. Install Buzz on Windows with Git for Windows 2. Connect to a relay that has a repository with at least one pushed branch 3. Open **Projects** -> select the repo -> **Remote** 4. Observe empty tree; Sync/Clone shows the mangled-path / `command not found` error above ## Test plan - [x] Unit: `cargo test --manifest-path desktop/src-tauri/Cargo.toml credential_helper_config_value` (formatter covered on all platforms; no-op for Unix-style paths) - [x] Local Windows NSIS build + install of this branch - [x] Projects -> Remote / Sync against a Buzz relay repo succeeds on Windows after the fix --------- Signed-off-by: Bjorn de Jong <bcrdejong@users.noreply.github.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Bjorn de Jong <bcrdejong@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Will Pfleger <pfleger.will@gmail.com> |
||
|
|
b92a1f4bf4 |
chore(desktop): add AgentCreationPreview file-size override to unblock main CI (#3154)
`Desktop Core` is currently red on `main`, and every open PR that picks up current main inherits the failure. [#2630](https://github.com/block/buzz/pull/2630) added a shadow-root search-input autofocus effect to `AgentCreationPreview.tsx`, taking the file from 999 to 1026 lines. It sat one line under the 1000-line default beforehand, so that PR's own CI was green while the merged file crossed the cap with no override entry in `desktop/scripts/check-file-sizes.mjs`. This adds the missing entry at 1026, following the pattern the rest of the overrides list uses. The split stays queued along with the others. ``` - src/features/agents/ui/AgentCreationPreview.tsx: 1026 lines (limit 1000) ``` The override is tight in both directions: at `1026` the gate passes, and at `1025` it reproduces the failure above. Signed-off-by: Will Pfleger <pfleger.will@gmail.com> |
||
|
|
8bb43d5191 |
fix(desktop): make the test loader work on Windows (#2758)
The resolve hook hands nextResolve absolute filesystem paths. Node's ESM resolver requires URLs or relative specifiers: POSIX absolute paths happen to be coerced, but a Windows path like C:\... parses as a URL with protocol 'c:', so every desktop unit-test run on Windows dies immediately with ERR_UNSUPPORTED_ESM_URL_SCHEME - on a clean tree, before any test executes. CI never sees it (Linux runners). Convert absolute paths to file:// URLs (pathToFileURL) at the three nextResolve call sites. On POSIX the resulting URL is identical to what node coerced before; on Windows the loader now works. With this change the full desktop suite (318 files, 3487 tests) passes on Windows 11 / node 24.14.1. Independently reported by another Windows contributor in #2634's testing notes. Claude-Session: https://claude.ai/code/session_01YFkHsUe1UUBBuvL81Zoe3n --------- Signed-off-by: technicallybrantley <77166260+technicallybrantley@users.noreply.github.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Will Pfleger <pfleger.will@gmail.com> |
||
|
|
545bb46b82 |
fix(desktop): make lint and unit-test gates work on Windows (#2943)
## Summary
On a Windows checkout the desktop quality gate does not work. This fixes
four defects in it. Two checks report success without examining
anything, one fails on every file, and one reports violations that its
own allowlist already covers.
**1. `pnpm test` finds no tests and still exits 0.** The `test` script
quotes the glob with single quotes. On Windows pnpm runs scripts through
`cmd.exe`, which does not strip single quotes, so node receives them as
part of the pattern and matches nothing. The run prints `# tests 0` and
exits 0 — a silent green. Double quotes are stripped by `cmd.exe` and by
POSIX shells alike, so Linux CI behaviour is unchanged.
**2. Every text file is checked out as CRLF.** There is no
`.gitattributes`, and `core.autocrlf=true` is the Git for Windows
default. Biome formats with LF, so `biome check .` fails on 1632 of 1633
files. `desktop/src/features/messages/ui/virtuaWheelModePatch.test.mjs`
fails too, because it matches `patches/*.patch` with `\n`-joined
patterns. The stored blobs are already LF, so `eol=lf` adds no
renormalisation churn — `git status` stays clean after the change.
**3. `check:px-text` never finds its own allowlist.**
`scripts/check-px-text-core.mjs` builds the key from `path.relative`,
which returns `\` separators on Windows, while the allowlist in
`desktop/scripts/check-px-text.mjs` is written with `/`. Nothing
matches, so the check reports 5 false violations on a clean tree.
**4. `check:file-sizes` examines nothing at all.** `findRule` compares
against `` `${rule.root}${path.sep}` ``. The roots are multi-segment
(`src/app`, `src/features`, `src-tauri/src`), so on Windows `src/app\`
never matches `src\app\...`. No rule matches any file: the check walks 0
of 1097 files and exits 0.
`scripts/check-pubkey-truncation-core.mjs` already normalises paths this
way (`relativePath.split(path.sep).join("/")`). This applies the same
idiom to the other two.
### Related issue
None found — no open issue covers this. The closest open PR is #2758,
which fixes a fifth Windows defect in `desktop/test-loader-hooks.mjs`;
it is required before the desktop unit tests can pass here, and it does
not overlap with these files. I checked the changed-file list of every
open PR: none touch `.gitattributes`, `desktop/package.json`,
`scripts/check-px-text-core.mjs` or `scripts/check-file-sizes-core.mjs`.
### Testing
Windows 11 (10.0.26200), node 22.17.1, pnpm 11.4.0, clean checkout with
the default `core.autocrlf=true`.
| Command | Before | After |
| --- | --- | --- |
| `pnpm test` | `# tests 0`, exit 0 | 374 test files discovered, exit 1
|
| `biome check .` | 1632 of 1633 files fail | 1633 checked, 0 errors |
| `pnpm check:px-text` | 5 false violations | passes |
| `pnpm check:file-sizes` | 0 of 1097 files examined, exit 0 | 1097
examined |
`check:file-sizes` now reports `src-tauri/src/managed_agents/runtime.rs:
2220 lines (limit 2216)`. That violation is pre-existing and not
introduced here — `main` currently fails on the same line in CI (Desktop
Core, run 30185213010, commit
|
||
|
|
313f793c87 |
feat(desktop): add search to agent emoji picker (#2630)
## Why The agent avatar picker disables Emoji Mart search, making emojis difficult to find when creating or editing an agent. ## What - Enable sticky search in the shared agent avatar picker - Focus the search field when the Emoji tab opens - Add end-to-end coverage for search, focus, and selection ## Risk Assessment Low — this is limited to the desktop agent avatar picker and does not change avatar persistence or agent configuration. ## References - `pnpm --dir desktop test` — 3,448 passed - `pnpm --dir desktop typecheck` — passed - `pnpm --dir desktop exec playwright test tests/e2e/agents.spec.ts` — 20 passed --- **Update Jul 24, 16:28 EDT:** Completed `CONTRIBUTING.md` validation. ### Manual test 1. Create an agent and open Add avatar → Emoji. 2. Confirm the search field is focused and filters results. 3. Select an emoji and confirm it becomes the avatar. 4. Repeat while editing an existing agent. ### Validation - `just ci` — passed - `just test` — passed ### Follow-up work None. Generated with Codex |
||
|
|
be275cfc6c |
fix(desktop): keep identity key help dialog readable in dark mode (#2854)
## Problem With macOS in dark mode (the default for fresh profiles is to follow the system scheme), the onboarding "What's an identity key?" help dialog renders its title in near-white on the always-white textured card, making it unreadable. The body paragraphs stay readable because they use the fixed olive `--buzz-onboarding-backup-ink`; only the `text-foreground` title (and the close button's hover color) flip with the theme. ## Cause The dialog's `DialogContent` carries `buzz-onboarding-neutral-theme` but is portaled outside the `buzz-startup-shell` subtree, so in dark mode it matches `.dark .buzz-onboarding-neutral-theme:not(.buzz-startup-shell)` (`components.css`), which flips `--foreground` to `0 0% 98%`. The textured powder card (`buzz-card-textured`) has no dark variant — it is baked light — so the near-white title disappears against it. ## Fix One attribute: pin the dialog to the light neutral theme with `data-system-color-scheme="light"`. This is the established pattern for always-light onboarding dialogs (`HostedCommunityOnboarding.tsx`, the `CommunityOnboardingFlow.tsx` avatar dialog), and the pinned-light CSS rule already exists and out-specifies the dark-mode flip. No new CSS. ## Testing - Added a dark-mode regression test to `tests/e2e/identity-key-help.spec.ts` (already registered in the Playwright smoke project): emulates `prefers-color-scheme: dark`, opens the dialog, and asserts the title resolves to the pinned light-neutral ink `rgb(23, 23, 23)`. Before the fix it rendered `rgb(250, 250, 250)`. - Manual repro: macOS appearance set to Dark → fresh profile → machine onboarding → click "What's an identity key?". Before/after screenshots are in the comment below. Signed-off-by: Michael Pfister <pfista@gmail.com> |
||
|
|
f2fe3b63c2 |
feat(acp): title agent sessions from the agent and channel name (#3028)
ACP harnesses that name a session from the first text they receive all land in the same place: every managed Buzz agent opens with the identical `[Base] You are operating inside the Buzz platform…` framing, so the harness session list shows a wall of indistinguishable rows. Because sessions are keyed per channel, one agent active in several channels produces several of them. This sends the name out of band instead. `session/new` carries `_meta.sessionTitle` with `Agent · #channel`, composed from the agent's `display_name` (or its unique `name` handle) and the channel it is serving. The prompt is untouched — no tokens spent, no perturbation of the prompt contract, and nothing new for the desktop observer's section parsing to handle. The mechanism is harness-agnostic: Buzz sends the field on every ACP `session/new` regardless of which harness is behind it, and adapters that don't read it ignore it per spec. ## Inert until a consuming adapter ships ACP adapters ignore `_meta` members they do not recognize, so against an adapter with no reader a Buzz session gets no title and nothing else changes. Three adapter halves consume it — Codex, Goose, and Claude Code (linked below); this half and each reader are only useful together, and each reader lands independently. No version floor is added. `codex_adapter_is_outdated_with_path` already gates codex-acp on major version `>= 1` (`desktop/src-tauri/src/managed_agents/discovery.rs:1276-1284`) and this feature needs nothing above that — an older adapter is not broken by the extra member, it simply ignores it. ## What changes **`crates/buzz-acp`** owns sanitization and composition. `sanitize_session_title` collapses whitespace, drops control characters, and caps at `SESSION_TITLE_MAX_CHARS` (80) by character, not byte, so a multi-byte character cannot be split. `compose_session_title` truncates only the channel part against that cap, so the agent name always survives; when the agent name alone fills the cap the channel is dropped rather than the name. `session_new_full` sets `_meta.sessionTitle` when a title exists and omits `_meta` entirely when it does not, since an adapter may distinguish an absent member from a null one. **`desktop/src-tauri`** only resolves and exports. `resolve_session_title` picks `display_name` or falls back to `name`, and `spawn_agent_child` writes it to `BUZZ_ACP_SESSION_TITLE` — or removes the variable when neither candidate yields anything printable. DMs, unresolved channels, and heartbeat sessions get the bare agent name with no channel suffix. ## Four properties that are easy to remove by accident **Control characters are stripped at the desktop boundary, not in the harness.** An interior NUL cannot cross the environment boundary at all — `Command::env` fails the entire spawn rather than passing it through. Deferring the strip to `buzz-acp` would let a corrupted display name turn display chrome into a spawn failure. A display name that is *only* control characters falls back to `name`. **The title is hashed into `spawn_config_hash`.** Without it, renaming an agent left the running process with a stale title and no restart badge. The hash runs the same `resolve_session_title` the spawn writes, and skips it when a user env override shadows `BUZZ_ACP_SESSION_TITLE` — spawn writes the title *before* the layered user env, so the override is what actually runs, and it already reaches the hash through `descriptor.env`. Hashing the record-derived value under an override would badge a rename that changes nothing. **One channel resolve serves both consumers.** `resolve_new_session_channel_context` returns `(is_dm, title_channel)` from a single metadata lookup, feeding both the canvas block's DM check and the title. `ChannelInfoResolver` caches only `Some`, so two independent calls against an unresolvable channel pay the full `fetch_channel_info` retry sequence twice — two timeouts plus a retry delay each — directly in front of `session/new`, precisely when the relay is already degraded. **The `"unknown"` channel name is treated as absent.** `fetch_channel_info` substitutes the literal `"unknown"` for a metadata event with no `name` tag. Composing that sentinel would title every unnamed channel `Agent · #unknown`, reintroducing the exact collision the suffix exists to remove while naming a channel something it isn't. The startup cache already refuses `channel_type == "unknown"` for the same reason. Closes #2334 Related — the adapter halves that consume `_meta.sessionTitle`: - [codex-acp#338](https://github.com/agentclientprotocol/codex-acp/pull/338) — Codex - [aaif-goose/goose#10712](https://github.com/aaif-goose/goose/pull/10712) — Goose - [claude-agent-acp#920](https://github.com/agentclientprotocol/claude-agent-acp/pull/920) — Claude Code --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> |
||
|
|
31e2de1966 |
fix(deps): bump nostr to 0.44.6 for RUSTSEC-2026-0216 (NIP-44 remote DoS) (#3135)
## Summary `cargo-deny` started failing on **every** PR and on `main` when **RUSTSEC-2026-0216** was published mid-afternoon today. Nothing in the tree changed — cargo-deny fetches the advisory DB at run time, so main's own `Security` job passed at `00ecf2c` and then began failing on the same commit. ``` error[vulnerability]: Remote Denial of Service via malformed NIP-44 v2 payload Cargo.lock:432 nostr 0.44.3 — RUSTSEC-2026-0216 advisories FAILED, bans ok, licenses ok, sources ok ``` The `nostr` NIP-44 v2 decrypt path reads a 2-byte unpadded-length prefix via `buffer[0..2]` **after** the HMAC check passes, without verifying the decrypted buffer holds 2 bytes. A sender who holds the conversation key — i.e. any DM sender — can craft a payload that decrypts to 0 or 1 bytes and panic the receiver. Remote DoS through any relay that delivers the event. No key material, plaintext, or memory corruption. Affects `0.26.0` through `0.44.4`. Fixed in `0.44.5`. ## The change Lockfiles only, 6 insertions / 6 deletions. The manifest already declares `nostr = "0.44"` — a caret range — so `0.44.6` needs no `Cargo.toml` edit. | Lockfile | Before | After | |---|---|---| | `Cargo.lock` | 0.44.3 | 0.44.6 | | `desktop/src-tauri/Cargo.lock` | **0.44.4** | 0.44.6 | **The desktop lockfile is the part worth reviewing.** `desktop/src-tauri` is excluded from the root workspace (`Cargo.toml:31`), and the `Security` job runs `cargo-deny check` at the repo root — so it never sees that lockfile. It was pinning a vulnerable *and* yanked `0.44.4` that no CI check would ever have flagged. Desktop calls `nip44::decrypt` at `commands/identity.rs:495`. Credit to @Eva for catching this; I'd have shipped the root-only fix and left it sitting there. **This isn't optional maintenance.** `0.44.0` through `0.44.4` are all yanked on crates.io. `0.44.5` and `0.44.6` are the only live versions in our range — staying put isn't an available option. ### On the two extra lines in the desktop lockfile The desktop bump also repoints two existing dependency edges: ``` nostr-derive: syn 2.0.118 -> syn 1.0.109 tempfile: getrandom 0.4.3 -> getrandom 0.3.4 ``` I checked these rather than waving them through: **no packages are added or removed** — both versions were already present in the graph, so only which edge points where changed. The resolution is stable across repeated re-resolves, and a plain re-resolve without the bump produces zero diff, so this isn't pre-existing lockfile staleness leaking in. ## Verification At this commit, in a clean worktree off `origin/main`: - `cargo-deny check advisories` → **`advisories ok`**, exit 0. The same tree before the bump reported `advisories FAILED` with this advisory, so the check is doing real work, not passing vacuously. - `./scripts/run-tests.sh unit` → all five packages pass. - `cargo test -p buzz-core` 229/229, `-p buzz-cli` 250/250, `-p buzz-relay --lib` 750 pass / 1 fail — the sole failure `api::mesh_demo::tests::demo_join_forwarded_arm_round_trips_echo` is pre-existing and reproduces identically at unmodified `00ecf2c`. - `desktop-tauri-test` passed in the pre-push hook, which exercises the crate whose lockfile changed. ## Why not a `deny.toml` ignore Considered and rejected. This is a reachable panic triggerable by any DM sender, and buzz-acp agents decrypt DMs from arbitrary senders. Suppressing it would ship a live remote-DoS to every agent and client in order to make a dashboard green. ## Note on `spin` The yanked `spin 0.9.8` / `0.10.0` warnings in the same job are **not** what fails CI — the log has exactly one hard error, this one. They're `warning[yanked]`, and warnings don't fail the build. `spin` is also three levels transitive (`mesh-llm-host-runtime → mdns-sd → flume → spin`) under a dev-dependency, so it isn't ours to bump. Left alone deliberately. ## Follow-up Unblocks #3128 (relay-admin ban gate), which has a zero dependency-file delta and will inherit this cleanly once main is merged in. Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Co-authored-by: npub1jmc9dt2lyvzu3h0kxlwxt5zg4fxp9476awyxw6gwxn72g6cw7exqs64whm <96f056ad5f2305c8ddf637dc65d048aa4c12d7daeb8867690e34fca46b0ef64c@buzz.block.builderlab.xyz> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> |
||
|
|
654f384906 |
fix(desktop): read the newest pair-scoped harness log (#3134)
Harnesses became per (agent, relay) pair in #2122 and now write `agents/logs/{pubkey}__{sha256(relay_url)}.log` via `managed_agent_runtime_log_path`. `get_managed_agent_log` was never updated and still read the legacy `agents/logs/{pubkey}.log`, so agent profile → Runtime → Harness Log froze at each agent's last single-runtime line while live output accumulated in files the reader never opened. The reader now resolves the log through `latest_managed_agent_log_path`, which picks the most recently modified file belonging to the agent — pair-scoped `{pubkey}__*.log` or legacy `{pubkey}.log` — and falls back to the legacy path when the agent has no log on disk at all. Agents that have not restarted since the update keep working, and the panel follows whichever harness is currently writing. The response already carried `log_path`, so the panel header names the file being shown. Selection is deterministic: equal mtimes break toward the higher filename, and files belonging to other agents or without a `.log` extension are never candidates. `storage.rs`'s inline test module moves to a `#[path]`-included sibling `storage_tests.rs`, matching the existing pattern in `teams.rs` and `archive/mod.rs`. This drops both halves under the desktop file-size limit (1383 → 826 / 701), so the ratchet entries tighten instead of growing. Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> |