[upstream-sync] Merge block/buzz 769ac70b7..02f640bc4 (33 commits) - #23
Merged
Conversation
…block#5133) ## What Relay-only carve-out of the ingest half of block#4999: generic EVENT ingest now accepts kind:30179 (NIP-PMA private managed-agent config). One file, `crates/buzz-relay/src/handlers/ingest.rs`, 16 insertions / 15 deletions; **two semantic lines**, byte-identical to the ingest hunk of block#4999 at `6f486e88`: 1. `required_scope_for_kind`: 30179 requires `Scope::UsersWrite` — same arm as its public sibling 30177 and the other owner-authored NIP-AP kinds. 2. `is_global_only_kind`: 30179 is owner-global, keyed `(pubkey, kind, d-tag)`; a stray `h` tag must not channel-scope it. The rest is import reflow plus replacing the guard test with a positive one (`private_managed_agent_kind_is_owner_scoped_global_user_data`: asserts UsersWrite scope, global-only, no h-channel scope). ## Why the guard test can be retired The removed test (`private_managed_agent_kind_remains_rejected_until_atomic_ingest_exists`) pinned a stated precondition: *"must not enter generic EVENT ingest before privacy and aggregate CAS deploy."* Both halves are resolved: - **Privacy** — the author-only read gates for 30179 shipped to main with block#4593: `AUTHOR_ONLY_KINDS` membership, `req.rs` pre-filter + result gates, `count.rs`, `event.rs` fanout, and the bridge pre-filter (`bridge.rs:999-1000` returns `restricted: author-only kinds require authors=[self]` / 403). Only the author can read the event back. - **Aggregate CAS** — block#4999 settled generation as **advisory**: the `g` tag is shape-validated, never relay-enforced. Last-write-wins per coordinate is the contract of record (see the kind:30179 contract blurb in block#4999), so no CAS mechanism is pending on the relay side. ## Why this is inert to existing relays and clients - No production desktop code on main authors kind:30179 — the codec (`private_managed_agent.rs`) has zero non-test callers. This PR accepts a kind nobody can produce yet. - Content is opaque NIP-44 ciphertext to the relay; the relay never decrypts it. - Reads remain author-only via the already-shipped gates above. - Storage is the standard parameterized-replaceable path already exercised by kinds 30175–30178. No schema, config, or migration changes. ## Testing - Full `buzz-relay` package suite at this commit: 859 passed, 1 failed — `api::mesh_demo::tests::demo_join_forwarded_arm_round_trips_echo` (504 vs 200), which **reproduces identically on clean main `769ac70b`** with this change stashed; pre-existing/environmental, not introduced here. - New positive ingest test passes. - Pre-push hooks green (branch-skew, rust-tests, desktop-tauri-checks). ## Relationship to block#4999 block#4999 (relay-primary agent config, desktop half) stays DO-NOT-MERGE pending live relay receipts + real CI; once this lands and deploys, its live test simplifies to plain `desktop-standalone` against the real relay, and block#4999 rebases to drop its now-duplicate ingest hunk (identical bytes → trivial rebase). Originating thread: buzz://message?channel=06f13ed3-0557-4ac2-922c-1545dd00bf97&id=2a43b3b4933a2ea78b77088619251c061355f9b7b6dc29ea0d702193f2344149 ## Brownfield FTS note (review findings, operator-ruled non-blocking for this PR) Max and Sami independently identified that the FTS privacy skip-set is regime-dependent: migration 0008 installs the positive allowlist (`kind IN (0, 9, 40002, 45001, 45003)`) **only on an empty events table**; an already-populated database keeps the 0001/0005 negative skip-list (wrapped by 0014 to add 30350), which omits 30179 — so on such an installation this PR admits 30179 rows whose NIP-44 ciphertext gets indexed by `to_tsvector`. Sami measured both regimes against real Postgres (brownfield: 30179 INDEXED; fresh: NULL) and demonstrated the existing drift test only exercises the fresh regime. `schema/schema.sql:222`'s canonical literal is also the negative list and omits 30179. Migration dates put any relay deployed with data before 0008 landed (2026-07-13) in the brownfield class. **Scope of exposure (Sami's trace):** not a content leak — `event_visible_to_reader` / `is_author_only_event` gates hold on both search surfaces (`req.rs:725`, `bridge.rs:1770`), so foreign readers receive nothing. Lost is the storage-level NULL-tsv backstop plus FTS page budget burned on post-filtered hits. **Operator ruling (Tyler, events `1472e5b6`, `cbd368ed`):** ship this PR without an exclusion migration. Safety argument that makes this sound rather than merely accepted: main has **zero non-test 30179 writers** until block#4999's desktop half deploys — no 30179 rows can exist, so nothing can be indexed in any regime while this PR is the only half live. **Additional review characterizations (Sami, non-blocking, on the record):** - *Behavioral delta enumerated:* routing triple (`required_scope_for_kind` / `is_global_only_kind` / `requires_h_channel_scope`) compared for all 65,536 kinds at base `769ac70b` vs head `77eeba6e` — exactly one row differs (30179). No other kind or client changes behavior. - *"SQL visibility before LIMIT" (NIP-PMA step 2):* no `AUTHOR_ONLY_KINDS` pushdown clause exists in `buzz-db` (only `SHARED_GATED_KINDS` has one). Author-only kinds are protected by the pre-filter (`author_only_filters_authorized`) plus post-filter omission; mixed-kind filters can burn candidate-page budget on discarded rows. Pre-existing and identical for 30300/30350 — not introduced here; noted so the NIP's step-2 checkbox is not read as fully ticked. - *Envelope validation gap:* 30179 is the only parameterized-replaceable kind at ingest with no per-kind envelope validator (codec grammar checks run in the desktop writer, not the relay). Generic limits only (256 KiB, ±15 min, pubkey==identity, d-tag bound). Self-inflicted footgun bounded to the author's own coordinate — candidate companion to the exclusion migration in the block#4999 rebase, deliberately not added here. **Bound follow-up (required before/with the block#4999 desktop half):** a 0014-shape additive migration (`pg_get_expr` capture + `CASE WHEN kind = 30179 THEN NULL ELSE (<existing>) END` wrap), add 30179 to the `schema/schema.sql:221` literal, and a brownfield-regime variant of the FTS drift test, per Sami's finding. Deploy-time spot check if ever wanted: `SELECT pg_get_expr(d.adbin, d.adrelid) FROM pg_attrdef d JOIN pg_attribute a ON a.attrelid = d.adrelid AND a.attnum = d.adnum WHERE d.adrelid = 'events'::regclass AND a.attname = 'search_tsv';` Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Co-authored-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz> Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
…lock#5136) ## Problem The harness posts each trial's task via `buzz messages send`, relying on `@<orchestrator-id>` name resolution. Task text is untrusted payload: when it contains @-tokens of its own, the CLI's mention resolver tries to resolve them as channel members, fails, and refuses to send — killing the trial with `RuntimeLaunchError` before the agent ever saw the task. Live occurrence: TB 2.1's `large-scale-text-editing` task embeds Vim macros (`:%normal! @a`). In the tb21-solo-1 run the trial died at launch: ``` RuntimeLaunchError: buzz messages send ... exited 1: {"error":"user_error","message":"mention '@A' does not match a current channel member; retry with --mention <pubkey>"} ``` Any TB task whose statement contains @-syntax is silently zeroed this way. ## Fix Pass the orchestrator's pubkey as an explicit `--mention` when posting the task. The CLI demotes unresolved @-tokens in the text to presentation-only when any explicit identity is supplied, so delivery still targets exactly the orchestrator and every @-token in the task statement becomes inert. The harness already holds the orchestrator's `AgentCredential` (it writes that pubkey into the worker roster tables), so no persistence is needed — fresh key per trial, fresh `--mention` per trial. Verified both halves against a live relay: a fenced `@a` without `--mention` still hard-fails (the resolver is not markdown-aware); the same content with an explicit `--mention` sends clean with `mention_pubkeys` containing only the target. ## Testing - `benchmarks/harbor-buzz-orchestra`: full pytest suite — 35 passed (34 baseline + new `test_send_mentions_by_pubkey_so_task_text_stays_inert`), ruff clean. Run against `origin/main` 769ac70 with exactly this patch applied. - `testbed`: full pytest suite — 23 passed, 1 skipped; ruff clean. ## Acceptance A task statement containing arbitrary @-tokens (Vim registers, emails, decorators) launches and delivers to the orchestrator instead of dying in `_send`. Originating Buzz thread: `buzz://message?channel=c3252dd2-0142-4e01-88c7-a2183c3960a5&id=74a65a0990fd2197882b66b5ea2707169d4a3dbd2020d1610c45150fb99f140b` Signed-off-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz> Co-authored-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
…uth extraction fails (block#4824) emit structured JSON diagnostics when NIP-OA owner-auth extraction fails during `buzz agents archive`/`unarchive` ## Problem When owner-auth extraction returned `None`, the CLI silently sent a bare request. The relay replied with `400: missing auth tag` and the caller had no way to know why extraction failed. ## Solution Extract `resolve_auth_from_profile` — a sync function that owns all three warning branches and the success path. `resolve_auth` reduces to: self-check → fetch kind:0 → delegate. - **Four distinct diagnostics**: no kind:0 profile / no tags array / `classify_owner_auth_tag` failure (typed `AuthFailure` enum: `NoAuthTag`, `AmbiguousAuthTag`, `WrongArity`, `NonStringElement`, `InvalidOwnerHex`, `InvalidSigHex`, `OwnerMismatch`) - **JSON format**: each fallback emits exactly one `{"warning":"..."}` line to stderr, matching the CLI's documented structured-stderr contract and the precedent in `channels.rs:597` - **Relay-supplied values** (target pubkey, actual owner pubkey) pass through `serde_json` serialization — no unescaped text - **Admin bare path preserved**: request is always sent after the warning; bare non-self requests are legitimate for relay admins - **Self path unchanged**: silent, no relay query ## Boundary tests Tests call `resolve_auth_from_profile` directly with `&mut Vec<u8>`. Each of the three production `writeln!` calls is covered: deleting any one fails at least one test. Success path asserts zero bytes written. ## Changes `crates/buzz-cli/src/commands/agents.rs` only: - `AuthFailure` enum with `message()` formatter - `classify_owner_auth_tag` returning `Result<[String;4], AuthFailure>` - `extract_owner_auth_tag` reduced to `#[cfg(test)]` `.ok()` wrapper - `resolve_auth_from_profile` sync helper (testable without `BuzzClient`) - `resolve_auth` reduced to self-check + fetch + delegate - 9 new boundary tests replacing the prior test-local helper --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
## Summary - move **Run on** into Advanced, directly after **Who can send instructions** - reuse the modal’s shared dropdown styling - give the Welcome guidance and composer matching glass treatment while preserving the corrected exit layering ## Validation - `pnpm -C desktop typecheck` - focused Playwright: Run on configuration (3 passed) - focused Playwright: Welcome onboarding flow (1 passed) - desktop unit suite (4,290 passed) --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Kenny Lopez <klopez4212@gmail.com> Signed-off-by: Watcher <bb7abfd757d0af7b66569d02ab9c0316b616f9d0c151ecf5b964344c462e7f8f@buzz.block.builderlab.xyz> Co-authored-by: Watcher <bb7abfd757d0af7b66569d02ab9c0316b616f9d0c151ecf5b964344c462e7f8f@buzz.block.builderlab.xyz>
   --------- Signed-off-by: kenny lopez <klopez4212@gmail.com>
## Summary - resolve the OAuth cache home directory with the repository's platform-aware `dirs` convention - preserve the existing `.config/buzz-agent/oauth` cache layout on macOS and Linux - make the cache-path regression assertion portable across path separators ## Problem `buzz-agent` read only `$HOME` when constructing the OAuth token cache path. Packaged Windows processes do not guarantee that Unix variable, so OAuth source construction failed with `oauth cache: $HOME not set` even though Windows had a valid user profile. ## Validation Independent reviewers validated exact commit `836d820483b141b7291170cb33535ac7cb49b2eb` on Windows/MSVC with `HOME` unset and `USERPROFILE` present: - `cargo +1.94.1 clippy -p buzz-agent --all-targets --locked -- -D warnings` - `cargo +1.94.1 fmt --all -- --check` - `git diff --check f53bbd1..836d820` - `auth::` tests: 11/11 passed with `HOME` unset - full package lib target: 396 passed / 2 failed; identical-base controls classified both as pre-existing Windows failures The changed regression fails on the base with `$HOME not set` and passes on this branch. `dirs 6.0.0` was already locked by other workspace crates; the lockfile change adds only the `buzz-agent` dependency edge. Signed-off-by: Kalvin C <kalvinnchau@users.noreply.github.com>
…ency (block#5130) Non-streaming LLM calls (`"stream": false`) through slow model/provider combinations routinely take longer than the fixed `BUZZ_AGENT_LLM_TIMEOUT_SECS` window (default 240 s) to return their first response byte. The retry loop then re-ran the identical 240 s bet three times, failed the turn, and the ACP harness requeued the whole turn from scratch: agents spent 30+ minutes producing nothing while every attempt died at the same wall. And because the LLM path only logged WARN lines on failure, a healthy-but-slow call was indistinguishable from a wedged one. ### Timeout handling - **Per-attempt escalation**: the per-request budget doubles after each timeout failure (`base × 2^n`, capped at `max(1200 s, base)` — `escalated_timeout()` in `llm.rs`), shared by the main `post()` loop and `openrouter_post()`. Non-timeout retryables (429/5xx/connect) do not escalate. A call that needs six minutes now succeeds on a later attempt instead of never. - **Per-request total timeouts**: enforcement moved from the client-level `read_timeout` to `RequestBuilder::timeout()` on each LLM request, so escalated budgets aren't silently floored by the shared client and each attempt's bound covers connect through body completion. Timeout error messages were updated to match the new semantics and still point at `BUZZ_AGENT_LLM_TIMEOUT_SECS`. ### Observability - One INFO line per completed LLM call: model, provider, `duration_ms`, `input_tokens`, `cached_input_tokens`, `output_tokens`. Slowness and prompt-cache effectiveness are now visible in harness logs without waiting for a failure, and `None` vs `0` token reports stay distinguishable. Handoff summarization calls log the same line with duration only. - The agent main loop wraps the call in a `session_id` tracing span so each line is attributable to a session. --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
## Summary - remove the post-creation private-key modal - return directly to the underlying page with one “Agent created” toast - preserve failed channel-attachment retry through an actionable toast ## Validation - desktop checks and E2E build - 4,392 desktop unit tests - focused Playwright coverage for standard, customized, and attachment-retry creation flows Signed-off-by: kenny lopez <klopez4212@gmail.com>
## Summary Replace Flutter's standard mobile pull-to-refresh indicator with our animated Buzz bee. ## Testing - `bin/just mobile-check` - `bin/just mobile-test` (1,248 tests) - Connected iPhone and Pixel 10 --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Fizz <50a12680c76f1a52c0b7af8dbb17e02c583227c290fb93b9a3defb456114223f@buzz.block.builderlab.xyz> Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Fizz <50a12680c76f1a52c0b7af8dbb17e02c583227c290fb93b9a3defb456114223f@buzz.block.builderlab.xyz> Co-authored-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
## Summary - replace reaction-only mobile sheets with an anchored long-press popover - add haptics, a dimmed frosted spotlight, and a spring-staggered reaction tray - preserve the full action sheet whenever other message actions are available - keep existing reaction pills outside the spotlight and make long press reliable across nested content ## Testing - `flutter analyze` - `flutter test` (1,248 tests) - signed iPhone release build installed and launched --------- Signed-off-by: kenny lopez <klopez4212@gmail.com>
## Summary - treat public starter-channel provisioning as best-effort after preserving the required private Welcome path - let community onboarding complete and focus Welcome when the reported metadata lookup error occurs - remove the now-obsolete retry-toast expectations for optional starter provisioning ## Scope This intentionally does not change relay tombstone semantics or auto-join existing public channels. ## Test plan - `pnpm exec playwright test tests/e2e/deep-link-invite.spec.ts` (8 passed) - `pnpm exec playwright test tests/e2e/onboarding.spec.ts --grep "failed public starter channel setup"` (1 passed) - `pnpm typecheck` - `pnpm check` - `pnpm test` (4483 passed) - pre-push hook: branch-skew, desktop-check, desktop-typecheck, desktop-test passed on `4658a07beb1e1d54443da5cd2e4a28fae0232f24` --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
block#3654) (block#4505) ## Summary - Heuristic and `--safe-rendering` now set `WEBKIT_DMABUF_RENDERER_FORCE_SHM=1` instead of `WEBKIT_DISABLE_DMABUF_RENDERER=1` - Legacy `DISABLE_DMABUF` stays owned so operators can still set `=0`/`=1` and take over the decision - Linux troubleshooting docs updated to match (block#3654) ## Test plan - [ ] unit tests in `webkit_rendering::tests` - [ ] On NVIDIA + WebKitGTK 2.52: workspace switch no longer SIGSEGVs where the distro NVIDIA guard does not fire (Debian/Ubuntu proprietary-NVIDIA may still crash — block#3654 stays open for that path) - [ ] `WEBKIT_DISABLE_DMABUF_RENDERER=0` still stands the heuristic down --------- Signed-off-by: Taksh <takshkothari09@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Alia <d32955ad69077062930cc46cfe2df30ca9aaf6f8e76422681265e9e9af704d78@buzz.block.builderlab.xyz> Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
## Summary - mirror the retained canvas terminal grid into a transparent, selectable text layer - preserve the canvas renderer and terminal focus behavior for ordinary clicks - reconstruct wide and combining glyphs correctly for clipboard text ## Why Buzz Term renders output entirely on a canvas and deliberately called `preventDefault()` on viewport mouse-down, so native selection and copy could not work. A canvas has no selectable text even if that cancellation is removed. The transparent text layer stays aligned with the visible cell grid, lets WebView native selection drive drag highlighting and copy, and follows active-session switches without changing the renderer or PTY protocol. ## Validation - `pnpm --dir desktop typecheck` - `pnpm --dir desktop test` — 4,373 passed - pre-push `desktop-check`, `desktop-test`, and `branch-skew` hooks passed on `1f2a3f8db63f6fe36b4a28bc911aea3c5186b2b0` --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
… tree (block#5142) ## Summary WebKit throws `SecurityError` from `localStorage.getItem` (not just `setItem`) when storage access is denied for the origin. With no `ErrorBoundary` in `desktop/src`, any such throw inside a provider render (`ThemeProvider`, `CommunitiesProvider`, `App` boot) propagated to the reconciler, unmounted the root, and left a blank window. Measured repro in block#5078: a single throwing `getItem` on `buzz-communities` or `buzz-active-community-id` kills the container. Closes block#5078. ## What changed **New helper — `desktop/src/shared/lib/safeStorage.ts`** - `getStorageItem(key, fallback?)` — wraps `window.localStorage.getItem`; on a thrown error (SecurityError under denied-storage origin) it warns once per key and returns the fallback. - `setStorageItem(key, value)` and `removeStorageItem(key)` — same fail-closed contract (return `false` on throw). - Unit tests in `safeStorage.test.mjs` cover the happy path and the `SecurityError` path. **Rewired the init-path readers that ran before any UI existed** - `desktop/src/features/communities/communityStorage.ts` — `migrateLegacyCommunityStorage`, `loadCommunities`, `loadActiveCommunityId`, `loadCommunityDiscoveryAfterLeave`, `initFirstCommunity` - `desktop/src/features/communities/legacyCommunityStorage.ts` — `migrateLegacyCommunityStorageBeforeRender` - `desktop/src/shared/theme/ThemeProvider.tsx` — `readStoredTheme`, `applyCachedVars`, the `useState` initialisers for `accentColor` and `followSystem`, and the accent re-read inside `applyTheme` **Root-level fence — `desktop/src/app/RootErrorBoundary.tsx`** - New top-level `ErrorBoundary` wrapping the whole provider tree in `main.tsx`. Any remaining uncaught render error (a future storage read that bypasses the helper, or any other render-time crash) renders a degraded splash with a Reload button instead of a blank window. ## Test plan - `desktop/src/shared/lib/safeStorage.test.mjs` — node `--test` runner, 11 assertions across healthy, absent, and SecurityError-throwing storage. - Full `just ci` runs on the blocker. - Existing `communityStorage.test.mjs` and `legacyCommunityStorage.test.mjs` continue to pass (they exercise the same functions via in-memory Storage doubles; the new code path in `migrateLegacyCommunityStorage` only adds a `try/catch` around the same body). ## Why not an ErrorBoundary-only fix A boundary alone can't help on a *clean* mount — the first throw already unmounted the whole subtree before any state or fallback data was loaded, so retrying would hit the same throw on the very next render. The storage accessor has to fail closed *and* the boundary has to exist for whatever bypasses it. Both are needed; neither is sufficient alone. --------- Signed-off-by: iroiro147 <sarthak.singh@mastersunion.org> 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>
…ion (block#5143) ## Summary WebKit throws `NotificationError` from the `Notification` constructor when the notification backend becomes temporarily unavailable (measured repro attached to block#5081). Every existing call site used `void sendDesktopNotification(...).then(...)` — discarding the returned promise with no rejection handler — so a throwing constructor became an unhandled promise rejection. The notification was silently dropped and the only trace was console noise. Closes block#5081. ## What changed Fenced the throw at the source inside `sendDesktopNotification` (`desktop/src/features/notifications/lib/desktop.ts`): - A new `try { ... } catch { ... }` wraps `new window.Notification(...)` and the `onclick` attach. - On catch, we `console.warn` once and `return false`, so the promise the call sites discard is always fulfilled with the same boolean result. No caller needs to change. ## Why at the source and not at each call site The issue body lists four rejecting edges: `useAppShellDesktopNotifications` (2×), `useReminderNotifications`, `use-feed-desktop-notifications`. Patching them one-by-one leaves the door open for the next consumer to make the same mistake — and the function itself advertises `Promise<boolean>`, so callers are entitled to assume the promise resolves with the delivery bit rather than rejects. Fixing the inside satisfies both properties for every present and future caller. ## Test plan - Behavior change is a guarded return value around a single constructor; unit coverage is best expressed inside the mounted-hook harness already used in the repro. Existing notification helpers (`shouldNotify*.test.mjs`) continue to pass. - Full `just ci` runs on the blocker. - The next notification after a backend blip delivers normally (the throw is per-call, not sticky). ## Note on scope This addresses the titled bug: unhandled rejection from a throwing constructor. A separate, intended follow-up is to wire a user-visible delivery-miss event if the platform exposes one — that's notification-observability work, not a } catch. --------- Signed-off-by: iroiro147 <sarthak.singh@mastersunion.org> 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>
…xtractor (block#5191) Replaces the four-helper auth resolution path with two focused functions and adds production async tests that count relay round-trips. **Before:** `resolve_auth` called `resolve_auth_from_profile` (warn-emitting probe into a throwaway sink) → `resolve_auth_deciding` (re-classified the same profile) → `handle_auth_failure` → `auth_failure_detail` (third classification). `Option<Option<&Value>>` encoded a sentinel for unreachable state; tests exercised only the pure sync helper, not the actual fetch count. **After:** - `extract_auth(profile, target, signer) -> Result<[String;4], AuthFailure>` — pure typed extractor; `AuthFailure` now covers `NoProfile` and `NoTagsArray` inline, no separate helper needed - `resolve_auth()` is now the linear state machine: self-check → fetch + extract → on failure: fetch again → route final `Err` to `CliError::Usage` (default) or one admin warning (`--admin`). No throwaway sinks, no duplicate classification, no sentinel type. - Five async tests drive the production resolver through a counted Axum test server on `POST /query` and assert on both return value and exact fetch count: first success (1), retry success (2), double failure / no `--admin` (2 + `Err`), double failure / `--admin` (2 + `Ok(None)` + one warning), self path (0). Two parser tests pin `--admin` on both `archive` and `unarchive`. - `--admin` short help text corrected to describe when the flag takes effect (after extraction fails, not unconditionally). 341 tests passing, clippy clean, fmt clean. --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
…g, activity feed polish (block#5073) ## Summary Follow-up batch on the Projects overview (continues merged block#1677): - **Repository access restrictions** — repositories the viewer can't reach are surfaced with a reason instead of failing silently. Channel-ACL denials (which arrive as the same 404 as a missing repo, for anti-enumeration) are re-classified using the repository's channel binding and the viewer's memberships (`useRepositoryAccess.ts`, `projectRepoAvailability.ts`). - **Projects loads in seconds instead of minutes** — enumeration no longer crawls every kind:5 deletion event on the relay. It fetches project/repo announcements first, then queries deletions scoped to those coordinates via chunked `#a` filters (3 queries instead of hundreds on staging). - **Activity feed layout polish** — bare event-type glyph beside the headline (no badge circle), timeline spine runs through the avatars connecting consecutive cards, linkable actor/project names are bold in theme foreground, rounded hover state, alignment fixes. - **Create button pinned** — the "+" create menu is pinned to the pane's top-right corner (equal 16px insets) and no longer scrolls away with the page header. - **List controls as a table header** — the scope selector (left) and sort + layout toggle (right) render as the first row of the list container on the Projects/Repositories/PRs/Issues tabs; in card view the identical bar stands alone with the cards below (`ProjectsListHeaderBar.tsx`). - **Repository rows show the git location** — subtitle is `github.com/org/repo` for external repos or `owner/repo` (resolved profile name) for Buzz-hosted ones, instead of repeating the project name (`repositoryDisplayPath`). - **Uniform work-item row heights** — issue rows previously ran the author chip in inline flow, letting the 20px avatar grow the line box ~3px taller than PR rows; both lists now share the same flex subtitle. 📸 Screenshots: [feed layout / pinned button](block#5073 (comment)) · [list header / repo subtitles / row heights](block#5073 (comment)). Note: two empty `chore: retrigger CI` commits exist on the branch from working around the Aug 6 GitHub Actions incident; happy to drop them with a signoff rebase before undrafting if preferred. Latest `main` is merged in (`a0cc35220`). ## Test plan - [x] Desktop unit tests (4,493 pass after merging main), Biome, tsc - [x] New unit tests for scoped deletion enumeration and repo availability re-classification - [x] New unit tests for `repositoryDisplayPath` (external, Buzz-hosted, unresolvable) - [x] Screenshot verification of feed layout, connector spine, and pinned button (top + scrolled states) — posted to the PR - [x] Screenshot verification of the list header row (list + card), repo subtitles, and matching PR/issue row heights — posted to the PR - [ ] Manual pass against staging (projects list load time, restricted-repo states) --------- Signed-off-by: Thomas Petersen <thomasp@squareup.com>
## Problem In the **Edit channel** dialog, flipping visibility (Public <> Private) persisted **immediately on selection**, bypassing the **Save changes** button — while every other field (name, description, temporary, TTL) waited for an explicit save. This surprised users and gave no chance to cancel a flip, e.g. a private->public change that instantly exposes channel history. Reported in the Buzz "Welcome" channel by Kevin Chung. ## Root cause The visibility dropdown was wired to `handleConvertVisibility()`, which called the update mutation on selection. This was intentional at the time (there was even an e2e test named `02 — visibility updates immediately` and an "Updating…" spinner), but it is inconsistent with the rest of the dialog and is the surprising behavior reported. ## Change (defer to Save) - Visibility becomes a **deferred draft** like the other fields: selecting a value updates local `isPrivateDraft` and marks the draft dirty. The change commits via `handleSaveChannelEdits` (which already handled visibility) on **Save**, and is discarded on **Cancel**. - The dialog title now reflects the **pending draft** (`nextVisibility`), so the pending choice is visible before saving. - The edit-dialog reset restores `isPrivateDraft` from server state. - Removed the now-dead `handleConvertVisibility` handler, `isConvertingVisibility` state, the `channelIdRef` race guard it needed, and the unused `isPending`/"Updating…" spinner path in `ChannelPermissionsSettings` (no caller passes `isPending` anymore). ## Tests - Rewrote e2e `02` -> **`visibility defers to Save`**: select -> Save enabled -> title reflects draft -> Save -> persists; toggling back to the original value clears the draft and disables Save. - Extended `09` (cancel discards drafts) to also cover a visibility change. - Repurposed `10`: the stale-update race it guarded is architecturally gone, so it now asserts an **unsaved visibility draft does not leak across a channel switch**. ## Validation - `pnpm typecheck` — clean - `biome check` (changed files) — clean - `pnpm test` — **4497 passed / 0 failed** - `playwright test --project=smoke channel-controls` — **10 passed** Signed-off-by: Kevin Chung <chung@squareup.com> Co-authored-by: Fizz <e3f95089179cc1bcc68d70c334b9bdf670d0470496db90bcdbb20386963432da@buzz.block.builderlab.xyz>
…5202) ## Summary - preserve each distinct agent pubkey in autocomplete even when agents share a persona or owner/name - continue to collapse duplicate source rows for the same normalized pubkey - show a truncated pubkey in the channel member-add picker so same-named instances are selectable ## Validation - `pnpm --filter buzz test` — 4,489 passed - `pnpm --filter buzz exec tsc --noEmit --pretty false` - `pnpm --filter buzz exec biome check src/features/agents/lib/agentAutocompleteEligibility.ts src/features/agents/lib/agentAutocompleteEligibility.test.mjs src/features/channels/ui/MembersSidebar.tsx` - independent validation by Fast Fizz on `509cb8d97b82f9708e24d4d59ad17c7b39516643`: typecheck, focused Biome, 22/22 focused tests, and `git diff --check` Generated by Hardworking Honey. --------- Signed-off-by: Hardworking Honey <c5c455215c2506cb8ba776518cec804af62d3a0526e32d496a22072e395042b9@buzz.block.builderlab.xyz> Co-authored-by: Hardworking Honey <c5c455215c2506cb8ba776518cec804af62d3a0526e32d496a22072e395042b9@buzz.block.builderlab.xyz>
…rized, ACP v2 messageId (block#5195) Three pre-existing gaps in the buzz-agent observer feed fixed together per Will's ruling ("all 3 in the current PR"): 1. **OpenAI/DBv2-GPT route** — `responses_body` never requested `reasoning.summary`; GPT-family models billed thinking tokens but returned `summary: []`. 2. **Anthropic/DBv2-Claude route** — `anthropic_thinking_config()` never sent `thinking.display`; newest Claude models (Opus 5, Sonnet 5, Fable 5, Mythos 5, Opus 4.7/4.8, Mythos Preview) default to `display:"omitted"`, returning thinking blocks with an empty `thinking` field — observer rendered nothing. 3. **ACP v2 compliance** — buzz-agent negotiates ACP v2 but emitted `agent_thought_chunk` and `agent_message_chunk` without `messageId`, which ACP v2's `ContentChunk` requires (`messageId` + `content` both required at schema head `d13d1baa`). ## Changes **`crates/buzz-agent/src/config.rs`** - New `ThinkingSummary` enum (`Auto`/`Concise`/`Detailed`) with `BUZZ_AGENT_THINKING_SUMMARY` env var (default `Auto`); mirrors `BUZZ_AGENT_THINKING_EFFORT` pattern - `anthropic_thinking_config()` now emits `"display": "summarized"` in both the adaptive shape and the manual-budget shape whenever thinking is enabled - Rewrote `is_adaptive_thinking_model` and `anthropic_thinking_config` doc comments to match Anthropic's exact three-way per-model terminology (doc: https://platform.claude.com/docs/en/build-with-claude/thinking-troubleshooting#supported-models): - Opus 4.6/4.7/4.8, Sonnet 4.6: **Off** — thinking OFF by default; `type:"adaptive"` required to enable - Opus 5, Sonnet 5: **On** — thinking on by default, can be disabled; we still send `type:"adaptive"` to activate `output_config.effort` - Fable 5, Mythos 5, Mythos Preview: **Always on** — thinking cannot be disabled; we still send `type:"adaptive"` to activate `output_config.effort` **`crates/buzz-agent/src/llm.rs`** - `responses_body` emits `reasoning.summary` alongside `reasoning.effort` when effort is set (gated — no bare `reasoning:{summary}` without effort) - Covers both the pure-OpenAI Responses path and the DBv2 GPT-family Responses path **`crates/buzz-agent/src/agent.rs`** - `agent_thought_chunk` carries `"messageId": format!("{run_id}-thought-{round}")` - `agent_message_chunk` carries `"messageId": format!("{run_id}-message-{round}")` - The two IDs are distinct (thought and assistant are two logical messages per the ACP v2 Message ID RFD) - `run_id` is a fresh random token per `session/prompt` invocation so IDs are session-unique across multiple prompts **`crates/buzz-agent/src/lib.rs`** - `run_id` plumbed into `RunCtx` (was already generated in `run_prompt`, just not threaded through) **`crates/buzz-agent/tests/golden_transcripts.rs`** - `test_acp_v2_chunks_carry_message_id` — negotiates v2, drives two consecutive `session/prompt` calls, asserts: both chunk types carry non-empty `messageId`; thought and message IDs are **distinct**; IDs do **not** recur across the two prompts in the same ACP session **`desktop/src-tauri/src/managed_agents/env_vars.rs`** - `BUZZ_AGENT_THINKING_SUMMARY` added to `is_safe_to_reveal` allowlist **`desktop/src-tauri/src/commands/agent_config_tests.rs`** - Tests for `BUZZ_AGENT_THINKING_SUMMARY` allowlist entry (case-insensitive) ## Tests added - `parse_thinking_summary_round_trips_all_values` - `parse_thinking_summary_unset_and_empty_yield_auto` - `parse_thinking_summary_is_case_insensitive` - `parse_thinking_summary_rejects_unknown_value` - `thinking_summary_as_str_mapping` - `responses_body_summary_present_iff_effort_set` - `responses_body_emits_configured_summary_mode` - `responses_body_concise_summary_mode` - `anthropic_thinking_config_adaptive_emits_display_summarized` - `anthropic_thinking_config_manual_budget_emits_display_summarized` - `test_acp_v2_chunks_carry_message_id` (integration test — two-prompt cross-session case) ## Notes - **DBv2 gateway parity for `display`**: unverified — the DBv2 Claude route proxies Anthropic Messages shape, but whether the gateway passes `thinking.display` through is not confirmed. Flagged here rather than blocking on it. - buzz-acp and Desktop TS are unchanged — they already parse `messageId` as optional and will pick it up from the wire automatically. - Chat Completions and OpenRouter paths: untouched. --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
## Overview **Category:** improvement **User impact:** Link previews appear in the composer and travel as privacy-safe sender-authored snapshots, so recipients never contact the linked site merely by opening a conversation. **Problem:** Cold-cache link paste could freeze the composer before the URL painted; recipient-side unfurling leaked visits; invalid or unresolved preview work could interfere with sending or leave dead cards behind. **Solution:** Paint pasted links before starting cold resolver work, resolve only in the sender's composer, attach only complete validated snapshots at Send, and render authored snapshots without recipient fallback fetching. ## Behavior - **Cold paste stays responsive:** bare and angle-bracket URL paste paths commit the visible link before resolver work begins. - **Sender-only fetching:** metadata is resolved while composing; recipients render only the sender-authored snapshot. - **Send never waits:** pending, failed, invalid, and unsendable previews are omitted. They do not block or cancel the message. - **Terminal misses disappear:** failed, timed-out, or 404 resolver results remove the composer card while preserving visible link text. - **Display-text links work:** Markdown links such as `[review the pull request](…)` produce and send the same snapshots as bare URLs. - **Compact and Rich presentation:** Compact remains the default; Rich preserves source description line breaks and paragraphs. - **Immediate draft-wide dismissal:** clicking × immediately hides all previews for the draft, suppresses links pasted later, and emits only `["link-preview", "none"]`. No confirmation detour. Suppression resets after send or clearing the draft. - **Zero recipient fallback:** missing, stale, malformed, off-relay, unsupported, or suppressed snapshots remain ordinary visible links; recipients never regenerate them. ## Implementation - Resolve previews from deferred composer URL state so paste can paint first. - Upload finished preview media to the active community relay and snapshot only valid, sendable media references. - Atomically capture ready snapshots at submit time; never append a late preview after send. - Validate snapshot and suppression tags in desktop/native and relay ingestion, rejecting duplicate or mixed forms. - Render composer previews as stable 55px attachment cards at desktop and narrow widths. - Add deterministic E2E coverage for cold paste, ready/pending/failed/invalid previews, display-text links, multiline Rich descriptions, immediate dismissal, later-pasted links, and suppression reset. ## Validation Validated head: `9807ba8952f190e76153834abf8ab61dd40be5e2` - Push hooks passed: `check-push-org`, branch skew, desktop check, mobile tests, desktop tests, Rust tests, and desktop Tauri checks. - Focused screenshot E2E at the validated head: 5/5 passed across Compact/Rich composer and recipient states, 800px/420px geometry, display-text links, multiline descriptions, and immediate dismissal. - PR CI was triggered for this exact head and is currently running; completed checks are green at the time of this update. - Worktree is clean and both PR head and validated branch resolve to `9807ba895…`. ## Screenshots ### Compact composer | Loading | Ready | |---|---| |  |  | ### Rich composer | Loading | Ready | |---|---| |  |  | ### Responsive composer | 800px loading | 800px ready | |---|---| |  |  | | 420px loading | 420px ready | |---|---| |  |  | ### Recipient presentation | Compact | Rich | |---|---| |  |  | ### Display-text Markdown link | Composer | Recipient | |---|---| |  |  | ### Rich multiline description  ### Immediate dismissal | Before × | Immediately after × | |---|---| |  |  | --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
## Summary <!-- What does this change and why? --> block#3419 is a tauri bug (tauri-apps/tauri#15110), which is already fixed in tauri-apps/tauri#15596. All we need is bump the @tauri-apps/cli version to include the bug fix. ```sh pnpm update --filter ./desktop @tauri-apps/cli@2.11.4 ``` This pr simply includes the changes after running the update command. ### Related issue <!-- Fixes block#1234, or N/A. Before opening: search existing issues/PRs for duplicates — link the closest one, or say "none found". --> fix block#3419 close block#3436. this pr supersedes it. ### Testing <!-- How was this verified? UI change? Include before/after screenshots (or a short recording). --> build the appimage and check the symlink in the appimage using `unsquashfs`. ```sh $ unsquashfs -o 944632 -ll /tmp/buzz/desktop/src-tauri/target/release/bundle/appimage/Buzz_0.5.4_amd64.AppImage | grep -i dirIcon lrwxrwxrwx root/root 8 2026-08-04 21:54 squashfs-root/.DirIcon -> Buzz.png ``` Signed-off-by: Tsung-Han Yu <14802181+johan456789@users.noreply.github.com>
…lders (block#4975) ## What users saw `buzz messages send` silently removed an explicitly supplied self-mention. The caller passed `--mention <sender-pubkey>` and received `accepted:true`, but the signed event had no matching `p` tag and `mention_pubkeys` was empty. ## Why it happened `nostr` 0.44 strips `p` tags matching the signer's pubkey by default. The codebase already opts out with `.allow_self_tagging()` for identity archive and unarchive requests, but the message and forum builders that accept mentions did not. The library therefore removed the tag during signing after the CLI had validated the explicit mention. ## What changed Added `.allow_self_tagging()` to all three event builders that accept mention tags: - `build_message` (kind 9) - `build_forum_post` (kind 45001) - `build_forum_comment` (kind 45003) An explicit mention now survives signing even when it matches the sender. ## How this was tested Added one regression test per builder. Each test signs with the same key included in the mention list and asserts that the resulting event preserves the self-referential `p` tag. Validation at `cd0f30bca`: ```text ./bin/cargo fmt --all -- --check cargo test -p buzz-sdk --lib cargo test -p buzz-cli --lib cargo clippy -p buzz-sdk -p buzz-cli --all-targets -- -D warnings ``` All 257 `buzz-sdk` tests and all 321 `buzz-cli` tests passed, and formatting and strict Clippy checks completed successfully. ## Scope and non-goals - Does not change mention validation, deduplication, or channel-member checks. - Does not change `normalize_mention_pubkeys`, which is not used by the messages-send path. - Does not add a dropped-mentions output field because the explicit tags are now preserved. Closes block#4906. --------- Signed-off-by: Brad Groux <bradgroux@hotmail.com> Signed-off-by: npub17q2gdupkvswvk5kprwc7plergm4gn295uw6fe4mjyjv53ahuhtnq02jd3f <f01486f036641ccb52c11bb1e0ff2346ea89a8b4e3b49cd772249948f6fcbae6@digitalmeld.communities.buzz.xyz> Signed-off-by: Brad Groux <3053586+BradGroux@users.noreply.github.com> Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: npub17q2gdupkvswvk5kprwc7plergm4gn295uw6fe4mjyjv53ahuhtnq02jd3f <f01486f036641ccb52c11bb1e0ff2346ea89a8b4e3b49cd772249948f6fcbae6@digitalmeld.communities.buzz.xyz> Co-authored-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
**Category:** fix **User Impact:** Mobile users who jump to Latest now see the newest message fully above the composer instead of partially hidden behind it. **Problem:** The channel message list treated the raw viewport bottom as the latest boundary even though the composer occupies part of that viewport. Latest jumps and follow-mode corrections could therefore place the newest message underneath the composer. **Solution:** Derive the latest alignment from the measured composer inset and use that same boundary for scrolling, follow detection, and layout correction. <img width="498" height="1008" alt="Screen Recording 2026-08-05 at 5 18 19 PM" src="https://github.com/user-attachments/assets/a7fc1a94-3ffb-4c34-908d-9bf4f3f082b4" /> <details> <summary>File changes</summary> **mobile/lib/features/channels/channel_detail_page/message_list.dart** Aligns Latest navigation and follow-mode correction with the visible bottom edge above the composer, and evaluates boundary state against the same geometry. **mobile/test/features/channels/channel_detail_page_test.dart** Adds a regression assertion that the newest live message clears the composer and that the Latest control disappears after navigation. </details> ## Reproduction steps 1. Open a mobile channel with enough messages to scroll away from the newest message. 2. Tap **Latest**. 3. Confirm the newest message is fully visible immediately above the composer and the **Latest** control disappears. 4. Resize the composer or keyboard while following latest and confirm the newest message remains above the composer. ## Tested fix The newest message remains fully visible above the composer after jumping to **Latest**.  ## Validation - `flutter analyze` — no issues - `flutter test` — 1,243 passed --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
## Buzz Desktop release v0.5.6 - **Frozen main:** `78c87ae20e182fffdd99744d6c9ff99df82b159c` - **Reviewed candidate:** `277d98a5cfb6d3b9af8b75122988f7a7df33ed5d` - **Previous desktop release:** `desktop-v0.5.5` - **Proposed immutable tag:** `desktop-v0.5.6` This PR may be **squash merged** after the Desktop Release Candidate check and all protected-branch checks pass. Merging authorizes publication of the exact reviewed candidate; later or unrelated changes on `main` cannot alter it. The checked-in changelog accounts for every non-merge commit in the release range. The Desktop tag points to the reviewed candidate commit, not the later squash commit. Publication remains bound to that immutable candidate tag. Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Release Automation <release-automation@users.noreply.github.com>
## Summary - treat provider `max_tokens` as an interrupted assistant response and continue the same turn with actionable feedback - discard tool calls from truncated responses, including malformed partial arguments, so they are neither executed nor replayed with invalid tool-result pairing - bound recovery to two retries while preserving normal finite `max_rounds` accounting ## Verification - `cargo fmt --all -- --check` - `cargo test -p buzz-agent` (422 unit tests plus all package integration/doc suites passed) - `cargo clippy -p buzz-agent --all-targets -- -D warnings` ## Notes The pre-push repository-wide hook also ran. Its Rust tests passed (2,270 passed, 14 ignored), but its `buzz-db` unit-test build was blocked because local rustc 1.89 is below sqlx 0.9's rustc 1.94 requirement. The affected package suite above is green on the exact pushed commit. Originating Buzz channel: `c3252dd2-0142-4e01-88c7-a2183c3960a5` Signed-off-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz> Co-authored-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz>
…block#5228) **Category:** fix **User Impact:** People who onboard by importing an existing key or recovering from a phone can now use "Skip for now" (and Next) on the harness setup and model config steps, instead of getting stuck. **Problem:** On the "Set up your agent harnesses" and "Configure your default model settings" onboarding steps, clicking **Skip for now** — or **Next** — did nothing for anyone who reached those steps by importing an existing key or recovering an identity from a phone. The app stayed frozen on the step. **Solution:** The onboarding state machine sets `continuingPubkeyRef` to the current pubkey on import/recovery to keep the flow on `onboarding` until setup finishes (added in block#4845). But `complete()` never cleared that ref, so once it matched the current pubkey the stage stayed pinned to `onboarding` forever — completion could never win. `complete()` now clears the ref so finishing/skipping actually settles the flow. Fresh-generated keys never set the ref, which is why first-run fresh-key skip already worked and the gap went unnoticed. <details> <summary>File changes</summary> **desktop/src/features/onboarding/machineOnboarding.ts** Clear `continuingPubkeyRef` inside `complete()` so an imported/recovered identity's "continuing" marker no longer outlives completion and pin the stage to `onboarding`. **desktop/tests/e2e/onboarding.spec.ts** Add a regression test that imports an existing key, reaches harness setup, clicks **Skip for now**, and asserts onboarding exits (reaches community onboarding). This fails without the fix. The existing skip tests only exercised the fresh-key path, which never set the ref — hence the gap. </details> ## Reproduction steps 1. Start onboarding and choose **Use an existing key** (or recover from a phone); import a key and continue to **Set up your agent harnesses**. 2. Click **Skip for now** (or **Next**). Before this change, nothing happens — the step is stuck. The same trap hits **Configure your default model settings**. 3. With this change, Skip/Next advances out of onboarding as intended. 4. Automated: `pnpm build:e2e && pnpm exec playwright test onboarding.spec.ts --project=integration -g "imported-key users can skip out of harness setup"` — passes with the fix, fails without it. ## Root cause Introduced by block#4845 (`feat(identity): recover desktop identity from a signed-in phone`), which added `continuingPubkeyRef.current === currentPubkey` as an independent condition selecting the `onboarding` stage. That guard has no off switch: `complete()` set the completion flag but never cleared the ref, so the OR'd condition kept the stage pinned. Not a revert candidate — the guard's intent (keep a just-published identity in onboarding until setup finishes) is correct; it just needed to release on completion. Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
…rride (block#5242) ## Problem Two v0.5.6-only regressions were introduced by block#4614 (the first enforced Tauri CSP): 1. **Tab-complete caret regression** — after tab-completing an @mention, #channel, or :emoji: shortcode, the cursor landed inside the inserted text instead of after the trailing space. TipTap inserts the correct text including the trailing space, but without its base stylesheet (`.ProseMirror { white-space: break-spaces }`) the trailing space collapses visually and the caret appears mid-name. 2. **Emoji picker unstyled** — the emoji-mart picker rendered as a giant unstyled layout (oversized search SVG, collapsed grid) because emoji-mart's shadow-root stylesheet injection was also blocked. Both symptoms have the same root cause. ## Root Cause Tauri's build-time asset processor scans `index.html` for inline `<style>` elements, injects a nonce token, and adds the corresponding `'nonce-…'` source to `style-src` at runtime. Per the CSP spec, **once a nonce is present in a directive, the browser ignores `'unsafe-inline'` for that directive**. `index.html` contained an inline `<style>` with the boot background color. When Tauri nonced it and injected `'nonce-…'` into `style-src`, the intended `style-src 'self' 'unsafe-inline'` became effectively `style-src 'self' 'nonce-…'` — blocking any runtime stylesheet injection not covered by a matching nonce: - TipTap's `injectCSS()` → `createStyleTag()` injecting `.ProseMirror { white-space: break-spaces; … }` - emoji-mart's shadow-root `document.createElement('style')` injection (Inline scripts follow a separate path — they are SHA-256 hashed, not nonced.) This only reproduces in packaged builds (where Tauri's custom protocol serves the HTML and enforces the policy). `tauri dev` loads from the Vite dev server and is not affected. ## Fix Move `html { background-color: #000; }` from an inline `<style>` in `index.html` to `desktop/public/boot.css`, linked via `<link rel="stylesheet">`. A linked stylesheet is not subject to Tauri's nonce injection, so `'unsafe-inline'` in `style-src` applies as declared. The `<link>` is render-blocking (same as the inline style was), so boot-flash behaviour is identical. **The production CSP string is unchanged.** This fix makes the policy apply as intended — no security properties are altered. Will's follow-up with the security team (Jordan Mecom / Eli Foster, authors of block#4614) is noted for post-ship. A Tauri-faithful CSP harness for the Vite dev path (so this class of regression is visible before a packaged build) is tracked as a separate follow-up. ## Files Changed - `desktop/index.html` — replace inline `<style>` with `<link rel="stylesheet" href="/boot.css" />` - `desktop/public/boot.css` — new file, the extracted `html { background-color: #000; }` plus rationale comment - `desktop/src-tauri/tests/csp.rs` — update comment: nonce for styles, SHA-256 for the boot script ## Testing - `just desktop-typecheck` ✅ - `just desktop-test` ✅ (4535/4535) - `just desktop-tauri-test` ✅ (all Rust tests including `csp.rs`) - Packaged validation: `pnpm tauri build --debug` completed; compiled binary bakes `style-src 'self' 'unsafe-inline'` with no nonce source injected ✅ --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
## Summary - serialize the relay error-message test with all other tests mutating the process-wide admission gate - clear its 300-second rate-limit expiry after the assertion - prevent the paused-time waiter test from observing another test's state ## Root cause `relay::tests::oversized_hint_is_capped_in_relay_error_message_string` arms the process-wide gate for 300 seconds without taking `TEST_SERIAL` or resetting it. In a parallel test run, `relay_admission::tests::concurrent_429_extends_the_window_for_parked_waiters` can observe that expiry, producing the reported `300.001s` instead of `5s`. ## Validation - focused admission suite + relay error test repeated 10 times - pre-push `desktop-tauri-checks` passed, including the full Rust workspace suite - `branch-skew` passed Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
## Buzz Desktop release v0.5.7 - **Frozen main:** `74b913cff8512c015dc6f1a7473b253fa803f954` - **Reviewed candidate:** `f167818d25dd9f03115ab907a16f07daee2ece5c` - **Previous desktop release:** `desktop-v0.5.6` - **Proposed immutable tag:** `desktop-v0.5.7` This PR may be **squash merged** after the Desktop Release Candidate check and all protected-branch checks pass. Merging authorizes publication of the exact reviewed candidate; later or unrelated changes on `main` cannot alter it. The checked-in changelog accounts for every non-merge commit in the release range. The Desktop tag points to the reviewed candidate commit, not the later squash commit. Publication remains bound to that immutable candidate tag. Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Release Automation <release-automation@users.noreply.github.com>
## What changed Bind the development Compose stack's published PostgreSQL, Redis, Adminer, Keycloak, MinIO, and Prometheus ports to `127.0.0.1`. ## Why Docker publishes a host port on every interface when no host address is specified. Running the development stack on a remote workstation or VPS therefore exposes its infrastructure services to that machine's public networks. Loopback bindings retain host-local development access and Docker's internal `buzz-net` connectivity without making those services Internet-reachable. ## Impact Local workflows continue using the same ports. Deliberate remote administration now requires an SSH tunnel or another trusted private-network path. ## Validation - `docker compose -f docker-compose.yml config --quiet` - Recreated the six affected services with their existing named volumes and Docker network - PostgreSQL remained healthy and retained all 54 application tables - Redis, MinIO, and Prometheus health checks passed - All affected ports were closed on the host's public IPv4 and IPv6 addresses while remaining available on loopback Origin: `buzz://message?channel=199eb7bc-3feb-484f-ae0e-4995123721ea&id=1c5bc387e86e21bb31677f56e1c862d4d9a17943bce91f8d93e825d029ce7f72` Signed-off-by: Paweł Karniej <karniej.p@gmail.com>
…starve the handoff summary (block#5248) ## Problem The handoff summarizer sends `max_tokens: 8192` (`HANDOFF_MAX_OUTPUT_TOKENS`) with no reasoning budget separation. On reasoning models, thinking tokens count against that cap: the model can spend the entire budget reasoning, length-stop with empty `content`, and `summarize()` — which only reads `content` — reports an empty summary. The handoff then degrades to lossy history truncation. Observed on deepseek-v4-flash during a terminal-bench 2.1 run (tb21-solo-3, 89 tasks): **13 consecutive handoff attempts across 5 trials failed exactly this way** (`handoff returned empty summary; truncating`), each burning ~3 minutes of full-cap reasoning, before a stochastically-short reasoning run finally fit. circuit-fibsqrt alone: 5 failures, 5 truncations, then success on attempt 6. video-processing failed its task by one frame after 3 context truncations. ## Fix `openrouter_summary_body` now grants reasoning its own equal-sized budget and excludes it from the response: - `reasoning.max_tokens = max_output_tokens` — thinking gets a dedicated budget instead of competing with the summary text - `reasoning.exclude = true` — reasoning is never in the response body; `summarize()` only reads `content` - `max_tokens = max_output_tokens * 2` — the total cap covers both budgets, so the text budget the caller asked for is actually available for text Non-reasoning endpoints ignore the `reasoning` object. Deliberately not paired with `provider.require_parameters`, for the reasons documented at `apply_openrouter_mutations` (it hard-404s valid model ids). The prior test `openrouter_summary_carries_neither_reasoning_nor_provider` asserted `reasoning` absent from the summary body — that assertion guarded against *effort-based* reasoning leaking in from config (the body is built independently of `cfg`, which is still true and still tested: `reasoning.effort` stays unset). Replaced with `openrouter_summary_budgets_reasoning_separately_and_carries_no_provider`. ## Verification - `cargo test -p buzz-agent`: 422 unit + 110 integration tests pass at bb2fedd - `cargo fmt` / `cargo clippy -p buzz-agent --all-targets`: clean - Not yet validated against a live OpenRouter reasoning endpoint — the failing scenario needs a long-context session to trigger organically. Evidence for the mechanism is from run artifacts (13/13 empty-summary length-stops on deepseek-v4-flash) and OpenRouter's documented `reasoning.max_tokens`/`reasoning.exclude` semantics. --------- Signed-off-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz> Co-authored-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
**Category:** improvement **User Impact:** Users can create, discover, and import agents from one consistent Add agent dialog. **Problem:** Agent creation, discovery, and import were split across a dropdown and separate dialogs, making the Add agent flow fragmented. The existing E2E suite also continued targeting the deleted dropdown after the flows were unified. **Solution:** Route the new-agent card directly into a unified dialog with dedicated Create, catalog, and Import navigation, then update the affected E2E coverage to exercise that interface and its current empty state. <details> <summary>File changes</summary> **desktop/src/features/agents/ui/AgentDefinitionDialog.tsx** Supports rendering the agent definition form inside the unified Add agent experience while retaining the standalone dialog behavior. **desktop/src/features/agents/ui/AgentDefinitionDialogShell.tsx** Adds the shared shell used to present agent-definition content consistently in embedded and standalone contexts. **desktop/src/features/agents/ui/AgentDialog.tsx** Passes the revised dialog state and close behavior through the existing agent dialog entry point. **desktop/src/features/agents/ui/AgentsView.tsx** Connects the Agents page to the unified Add agent dialog and opens newly added catalog agents in their profile panel. **desktop/src/features/agents/ui/PersonaCatalogDialog.tsx** Combines catalog browsing, agent creation, and snapshot import behind persistent navigation, including dirty-navigation confirmation. **desktop/src/features/agents/ui/UnifiedAgentsSection.tsx** Replaces the new-agent dropdown with a direct Add agent entry point and adjusts the responsive card grid. **desktop/src/features/agents/ui/personaLibraryCopy.ts** Updates catalog-facing copy for the unified experience. **desktop/src/features/agents/ui/usePersonaActions.ts** Returns the resolved local persona after catalog activation so the caller can open the added agent. **desktop/tests/e2e/agent-readiness-screenshots.spec.ts** Opens the embedded create pane directly for readiness screenshots. **desktop/tests/e2e/agents.spec.ts** Covers unified Create, catalog, and Import navigation and asserts the current shared-agent empty state. **desktop/tests/e2e/global-agent-config-screenshots.spec.ts** Updates global configuration screenshot setup for direct create-pane entry. **desktop/tests/e2e/inline-custom-harness.spec.ts** Updates custom harness setup for the embedded create form. **desktop/tests/e2e/persona-env-vars.spec.ts** Updates environment-variable and model-provider scenarios for direct create-pane entry. **desktop/tests/e2e/persona-model-combobox-screenshots.spec.ts** Updates model combobox screenshot setup for direct create-pane entry. **desktop/tests/e2e/smoke.spec.ts** Updates agent-creation smoke coverage for the unified Add agent dialog. **desktop/tests/e2e/where-to-run-config.spec.ts** Updates provider-selection coverage for the embedded create form. </details> ## Reproduction steps 1. Open the Agents page and select the new-agent card. 2. Confirm the Add agent dialog opens directly on Create without an intermediate dropdown. 3. Use the left navigation to browse shared agents and open Import. 4. Select a catalog agent and confirm the dialog closes and the added agent's profile panel opens. 5. Run the affected desktop Playwright smoke and integration specs and confirm all scenarios pass. --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
Signed-off-by: adrienlacombe <6303520+adrienlacombe@users.noreply.github.com> # Conflicts: # crates/buzz-relay/src/handlers/ingest.rs # desktop/src-tauri/tauri.conf.json
Upstream block#3818 (rich link previews) renamed resetLinkPreviewTitleCache to resetLinkPreviewMetadataCache, so AGENTS.md's list of community-scoped singletons named a function that no longer exists. That list is what a future change consults before adding a module-level cache, so a stale entry there is a data-leak footgun across community switches, not a cosmetic typo. Also adds resetBackgroundMediaUploads, which was already wired into resetCommunityState but had never been listed. Signed-off-by: adrienlacombe <6303520+adrienlacombe@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Merges
block/buzz769ac70b7..02f640bc4— 33 commits, preserving both merge parents.What changed upstream
Desktop (162 files) — unified add-agent flows (block#5015), rich link previews on messages (block#3818), Projects follow-ups: access restrictions, fast loading, activity feed polish (block#5073), externalized boot
<style>to survive Tauri CSP nonce override (block#5242), imported/recovered identities now finish onboarding (block#5228), selectable terminal output (block#4980),WEBKIT_DMABUF_RENDERER_FORCE_SHMfor NVIDIA/AppImage (block#4505), release 0.5.6 → 0.5.7.Relay / core — accept kind:30179 private managed-agent events at ingest (block#4133), best-effort public starter channels (block#5192).
Agent / CLI — budget summarizer reasoning separately so it cannot starve the handoff summary (block#5248), recover from max-token response truncation (block#5223), escalate LLM timeouts per retry (block#5130), single typed extractor replacing the probe/decider/detail split (block#5191), structured JSON warning on archive/unarchive owner-auth extraction failure (block#4824).
Mobile (20 files) — anchored reaction popover (block#5025), bee pull-to-refresh (block#5059), keep latest messages above composer (block#4981).
Infra — bind development services to loopback (block#4871),
@tauri-apps/cli→ ~2.11.4 for the Linux app-icon fix (block#4858).Conflicts
Two, both the "each side added an entry to the same list" shape. Kept both sides in each.
crates/buzz-relay/src/handlers/ingest.rs— two hunks, from upstream block#4133 wiringKIND_PRIVATE_MANAGED_AGENTinto ingest while the fork carries the threeKIND_SPONSOR_*kinds in the same import list and the samerequired_scope_for_kindarm.KIND_SPONSOR_DEPLOY_REQUEST,KIND_SPONSOR_REQUEST,KIND_SPONSOR_RESULTin sort order.cargo fmt --checkpasses, so the hand-merged list matches what rustfmt would emit.required_scope_for_kind— addedKIND_PRIVATE_MANAGED_AGENTto theScope::UsersWritearm, keeping the fork's three sponsor kinds and theirFORK-LOCAL PATCHcomment intact. Upstream had collapsed the arm to a one-line brace form; kept the fork's multi-line form since it carries the comment.Not a kind collision.
KIND_PRIVATE_MANAGED_AGENT = 30179already exists in this fork'skind.rs(arrived in an earlier sync) and sits well outside the fork's reserved 30900–30999 block.crates/buzz-core/src/kind.rsis byte-unchanged by this merge. No wire-format change.desktop/src-tauri/tauri.conf.json— the standing brand-vs-version conflict. KeptproductName: BitcoinMarketsandidentifier: app.bitcoinmarkets.desktop, took upstream'sversion: 0.5.7(was 0.5.5). Deep-link scheme["bitcoinmarkets"]verified intact.Clean-merge review (a clean merge is not a correct merge)
Upstream touched three fork-patched files. All three re-read; every fork marker survived and still means what it did:
desktop/src-tauri/src/relay.rs— upstream only touched a test (reset_rate_limit_gate+TEST_SERIALserialization). Fork's release-build allowlist default andpub mod allowlist;intact.desktop/src-tauri/src/lib.rs— upstream addedmod link_preview_tags;, dropped a blank line, and renamedfetch_link_preview_title→fetch_link_preview_metadata. Fork's single patch (deep_link::is_supported_deep_linkin the single-instance argv filter) intact. File-size ratchet checked:lib.rsis 992/1000 andrelay.rs998/1000 —pnpm check:file-sizespasses, but headroom is now 2 lines onrelay.rs.desktop/src-tauri/tauri.conf.json— resolved above;externalBinunchanged, somacos-canary.yml's sidecar list still tracks upstream's non-Windows lanes.Also checked and clear: no new files under
migrations/(no duplicate version integers;migrations.len()assertion still 30 and passing),release.ymlandmacos-canary.ymluntouched by upstream, no new sidecar added to a lane this fork runs.AGENTS.md update
One correction, in its own commit. Upstream block#3818 renamed
resetLinkPreviewTitleCache→resetLinkPreviewMetadataCache, so AGENTS.md's list of community-scoped singletons named a function that no longer exists. That list is consulted before adding any module-level cache, so a stale entry is a cross-community data-leak footgun rather than a typo. Also addedresetBackgroundMediaUploads, already wired intoresetCommunityStatebut never listed.Verification
All run locally on this branch. Real results:
cargo fmt --all --checkcargo fmt --manifest-path desktop/src-tauri/Cargo.toml --all --checkcargo clippy --workspace --all-targets -- -D warningscargo clippy --manifest-path desktop/src-tauri/Cargo.toml --all-targets -- -D warningscargo metadata --lockedCargo.lockre-resolution neededscripts/test-release-ref-contract.shrelease ref contract passedscripts/test-mobile-worktree-overrides.shjust test-unitdart format --set-exit-if-changed .flutter analyzeflutter testpnpm check:file-sizes(desktop)Mobile gates ran against Hermit Dart 3.11.5 (confirmed via
dart --version, not the system 3.8.0).scripts/test-release-ref-contract.shhad to be run from a clean clone — it copies the repo includingtarget/(43 GB here), which fills the disk from the working checkout. Judged by exit code 0 and the finalrelease ref contract passed; the::error::lines it prints are its own negative tests.Needs a human look
This PR trips tripwire 4 — a conflict resolved in
crates/buzz-relay/src/handlers/ingest.rs. Left unmerged and labelledneeds-humanper the runbook, even though every gate is green.What to actually check, in priority order:
required_scope_for_kindresolution.KIND_PRIVATE_MANAGED_AGENTnow returnsScope::UsersWritealongside the fork's sponsor kinds. That is upstream's own intent for the kind, and the arm is shared, so the resolution is mechanical — but it is a relay authorization path, which is why it is a tripwire.relay.rsat 998/1000 lines. Two lines of headroom. The next upstream change touching it likely trips the ratchet, and the fork'spub mod allowlist;lives there becauselib.rshad no headroom. Worth deciding pre-emptively where the allowlist declaration moves next.identity.keyapp-data fallback carrying identity across an update is still listed as unverified. This release crossing two versions is a reasonable moment to actually test that.Nothing else. No wire-format change, no migration, no fork patch deleted, no kind renumber.
Merge with a merge commit, not squash. A squash drops the second parent, leaves the merge base stale, and forces every later sync to re-resolve these same conflicts from the same stale base — this fork has already been repaired by hand once for exactly that (
3ce7c8adc).