Commit Graph

1776 Commits

Author SHA1 Message Date
Logan Johnson
ef10f1882d fix(desktop): make lifecycle persistence credential neutral
Read current credentials without migration, reserve new-key writes for explicit mint/import, and merge lifecycle bookkeeping into raw storage. Revalidate named launch credentials at the shared post-teardown spawn boundary. Bind production credential revocation regressions through ordinary Stop, local Start, bulk and restore, preserving destination provisioning and inline fallback.

Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 22:34:35 -04:00
Logan Johnson
61943e15ec fix(desktop): clear native clippy gate and fresh retention scope init
Repair every clippy 1.95 -D warnings failure from CI run 34155454680
(Desktop Core 101846514710) without weakening a check: the test env lock
becomes a tokio Mutex (buzz-db POOL_METRICS_TEST_LOCK convention) so async
tests hold the same single lock domain across await via
lock_path_mutex_async while sync tests keep blocking_lock; the dead
spawn_agent_child wrapper is removed and test-only helpers are cfg(test)
gated with ownership notes; mesh-feature-used relay_mesh_model_id carries
a justified cfg_attr; identical placement branches merge; restore test
module moves after the last item; mechanical bool/borrow/slice fixes; two
documented ABI argument-count allows at the launch-inputs passthrough and
the Tauri invoke contract.

local_host opened the scoped retention db without ensuring the
agents/retention parent that active_retention_scope only creates lazily
at retention time, panicking every remote_credentials fixture before its
assertions and any first lifecycle message preceding retention on a fresh
install. Mirror remote_stop::connection's on-demand parent ensure with
the same contextual directory error.

All seven remote_credentials fixtures execute and pass on the authorized
no-default lane; workspace clippy -D warnings and rustfmt pass locally.

Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 16:22:12 -04:00
Logan Johnson
1c4428cb82 fix(desktop): restore captured session policy command writer
Post-policy CI found the shared launcher calls the pure captured-policy helper removed with the obsolete live-app wrapper. Restore only the command writer; keep captured preflight authority and no live state reads. Cover both policies overriding inherited environment.

Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 15:16:49 -04:00
Logan Johnson
b8d72f55da fix(desktop): use destination-local credentials for remote launch
Route signed lifecycle requests through ordinary async preflight and shared captured launch admission. Require an already provisioned matching local agent key, revalidate after awaits and teardown, and retain independent Stop.

Persist named launch lifecycle metadata without migrating or restoring captured credentials. Add isolated signed receiver and actual teardown regressions for revoked keys, missing executables, request expiry, and fresh same-host Start.

Native fixtures require Unix --no-default-features and remain unexecuted locally; dependency-complete CI owns compilation and runtime validation.

Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
12f75703b7 test(desktop): preserve host binding before moving configuration fixture
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
6cb9d2f0ca fix(desktop): repair native configuration integration compilation
Carry the identical producer-owned compiler repair without rewriting the published validation head. Preserve capture, preflight and admission semantics. CI run34144148717 diagnosed these errors; native validation follows on the successor head.

Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
a7d1357c84 fix(desktop): integrate lifecycle with captured launch admission
Use producer preflight authority and shared captured Start/Restart admission. Capture the existing explicit Start Stop fence and runtime generation before await; keep probes and Restart without resume authority and preserve Failed after destructive Stop. Broker remains provisioning-unavailable. Native compilation remains gated on CI-only draft authorization; local formatting and integrated frontend checks pass.

Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
a35d9497cd test(desktop): fence expired readiness after confirmed source Stop
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
3357948600 feat(desktop): bind lifecycle switching to exact runtime configurations
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
651cb1ba26 fix(desktop): retire lifecycle continuations when controls are canceled
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
ee6395b94e refactor(desktop): satisfy runtime integration file-size ratchet
Move the existing descriptor resolver and discovery record fixture into sibling modules without changing their behavior or dropping cases. The required CI changes job rejects their producer-added growth in inherited oversized files; no policy limits or allowlists changed. Native fmt and actual size ratchet pass; native compilation is still awaiting CI-only draft authorization.

Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
eb109b8a19 fix(desktop): require captured preflight authority for runtime launches
Recover shared Default/named launch capture; cover direct pair Start/Restart, bulk and restore with locked same-plan admission. Classify mesh recovery by running pair snapshots. Route UI Restart through native admission while preserving old-turn cleanup. Native orchestration regressions remain a dependency-complete CI gate; frontend6295 and bounded exact-source policy checks pass. Local checkpoint only; no publication or remote broker-policy change.

Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
7da7c39e64 fix(desktop): bind prepared launch inputs and enforce selected ACP model
Integrate ACP strict model verification with the named native caller. Bind app session inputs, require declared named MCP tools, and fence Default selection and restore preflight. Local checkpoint: native compilation and async workflow validation remain pending dependency-complete CI; no publication approval assumed.

Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
4ae32aa588 feat(desktop): checkpoint scoped runtime configuration launches
Local integration checkpoint; native compilation and strict ACP model enforcement remain validation gates. Scope and async prepared-launch APIs are stable for lifecycle integration; not publication-ready.

Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
546f5ec16a fix(desktop): preserve runtime community authority and refuse lossy receipts
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
ef58314dd5 fix(desktop): do not retry terminal receiver initialization
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
6bea46f78c fix(desktop): bound lifecycle receiver recovery without command replay
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
18258f91d5 fix(desktop): observe receiver closure after subscription readiness
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
a7d32b96c7 fix(desktop): diagnose and explicitly recover lifecycle receivers
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
cbb4091ef3 fix(desktop): preserve launch community separately from runtime aliases
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
943b62af7a fix(desktop): keep lifecycle receiver failures out of shell layout
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
7d00439d05 test(multiverse): scope Stop row and exercise mounted launch refusal
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
c6f3e3eb2f fix(multiverse): retain superseded Stop outcomes for exact retry
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
def4f23776 feat(desktop): mount Start Restart and Move controls
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
8b26bb20b5 feat(desktop): coordinate failure-final Move and exact lifecycle retry
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
30ce5b95f3 fix(multiverse): introduce provisioning stub with its receiver
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
a52c0537fc feat(desktop): receive authenticated Start and current-host Restart
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
faee0cacea fix(multiverse): defer provisioning stub until receiver slice
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
0f2eb99fee feat(desktop): introduce destination-local keyless launch boundary
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
b85c53293f feat(desktop): persist ordered placement and lifecycle admission
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
c6be5382f1 test(multiverse): exercise mounted Stop recovery and private fanout in CI
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
20436c81f2 feat(desktop): mount private remote Stop controls and live receiver
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
17e08d7465 feat(desktop): add scope-bound Stop client and exact-request retry
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
18a0d17070 feat(desktop): authenticate and durably consume remote Stop
Bind owner delegation, installation, agent and community before ordinary Stop. Persist admission before effects and retain exact signed outcomes for retries across reopening and bounded eviction.

Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
a5b36742cd feat(desktop): fence automatic launches after remote Stop
Bind explicit Start permission before preflight and release it only after child registration. Reuse ordinary platform-specific pair Stop and refuse success for an unscoped live legacy child.

Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
09282c7283 fix(desktop): defer capability changes until real time advances
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
0fdf436105 feat(desktop): report private per-Desktop runtime capabilities
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
23e8f850c0 fix(desktop): fence delayed pulses and order host observations
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:36 -04:00
Logan Johnson
e5812f053b feat(desktop): show private per-Desktop last-heard observations
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:35 -04:00
Logan Johnson
271fefd629 test(desktop): match unsigned relay ingest drift constant
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:35 -04:00
Logan Johnson
60dacc53a7 feat(desktop): list private known Desktops
Signed-off-by: Logan Johnson <loganj@squareup.com>
2026-09-07 12:59:35 -04:00
Wes
3c7f288c60 chore(release): release Buzz Desktop version 0.5.23 (#7381)
## Buzz Desktop release v0.5.23

- **Frozen main:** `dad5a33865fc81a2e55b3b60746632f615ec1e3a`
- **Reviewed candidate:** `b9392d9d78744df365f9276e1ffe8c1baa5ea903`
- **Previous desktop release:** `desktop-v0.5.22`
- **Proposed immutable tag:** `desktop-v0.5.23`

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>
2026-09-05 18:06:14 +00:00
Jeff Hedlund
dad5a33865 fix(desktop): keep packaged frontendDist relative so Windows embeds assets (#7177)
## Root cause

`tauri-command.mjs` points `frontendDist` at a `mkdtemp` directory so
concurrent OSS/internal packages cannot overwrite each other's assets.
On Windows that is an absolute path with a drive letter.

`FrontendDist` is an untagged serde enum whose **first** variant is
`Url(Url)`, and `C:\Users\...` is a valid WHATWG URL with scheme `c:`,
so serde selects `Url`. `tauri-codegen` then does:

    FrontendDist::Url(_url) => Default::default(),   // embed nothing

A missing *directory* panics with a clear message; a URL is silent. The
build exits 0 and produces an installable app with no frontend assets,
which boots to `ERR_FILE_NOT_FOUND` in the WebView.

Linux and macOS are unaffected — `/tmp/...` has no scheme, so it falls
through to `Directory`.

This affects every Windows build that goes through `pnpm tauri build`,
including `release.yml`'s NSIS job and `windows-canary.yml`.

## Fix

Pass the path relative to the config's own directory. `tauri-codegen`
resolves `frontendDist` with `config_parent.join(path)`, so a relative
path reaches the same directory and cannot parse as a URL. When the temp
directory is on another drive there is no relative form, so the scratch
root is created beside the config instead.

`BUZZ_PROTECTED_BUILD_OUTPUT` still receives the absolute path, and
cleanup is unchanged.

## Testing

`tauriCommand.test.mjs` asserted against the value it had just been
handed, so it could not observe this. Its fake CLI also resolved
`frontendDist` against the process cwd, which is not what Tauri does.

- Fake CLI now resolves against the config directory, matching
`config_parent.join(path)`.
- New case asserts the packaged `frontendDist` is not absolute and does
not parse as a URL. The absolute check is what fails on Linux/macOS, so
the regression stays covered on every platform.
- Verified the new case fails on the unpatched wrapper and passes with
the fix; the two existing cases pass either way.
- Desktop suite: 5844 passed. `useDocumentVisible` has a pre-existing
load-dependent flake that also reproduces on an unmodified checkout.
- Biome check clean on both files.

Verified end to end by rebuilding the Windows NSIS installer: embedded
asset keys in `buzz-desktop.exe` went from 0 to 490, and the app
launches.

---------

Signed-off-by: Jeff Hedlund <jhedlund@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-05 09:04:06 -06:00
Taylor Ho
f038cbbb0d fix(sidebar): simplify unread indicators and emphasize priority activity (#7134)
**Category:** fix
**User Impact:** The sidebar now presents unread activity consistently:
unread rooms are bold, offscreen activity is counted by destination, and
DMs or directed activity receive stronger emphasis.

**Problem:** Sidebar unread state was split across competing signals:
agent work could trigger overflow, message totals inflated its count,
non-DM rows showed redundant numerals, and routine room activity looked
as urgent as DMs or directed messages. This made the sidebar noisy and
made the overflow value harder to interpret.

**Solution:** Use one stable offscreen unread control that counts rooms
and DMs—not messages—and remove agent work from that signal. Keep
ordinary room activity quiet; promote DMs, mentions, broadcasts, and
relevant thread replies; preserve DM avatars and targeting; bold every
unread room; and retain thread dots while removing non-DM numerals.

<details>
<summary>File changes</summary>

**desktop/src-tauri/src/unread_catch_up.rs**
Classifies relevant thread replies as priority activity during native
unread catch-up so startup state matches live rendering.

**desktop/src/app/AppShell.tsx**
Passes the priority unread destination set into the sidebar.

**desktop/src/features/channels/useUnreadChannels.ts**
Projects unread destinations, priority state, and thread replies
consistently while keeping Dock badge behavior separate from sidebar
emphasis.


**desktop/src/features/sidebar/lib/useOffscreenActivityChannelIds.test.mjs**
Removes tests for the superseded agent/activity overflow projection.

**desktop/src/features/sidebar/lib/useOffscreenActivityChannelIds.ts**
Removes the old agent-plus-message overflow projection so agent work
alone no longer creates the unread indicator.

**desktop/src/features/sidebar/lib/useSidebarActivityOverflow.ts**
Replaces activity-volume overflow state with unread-destination overflow
state.

**desktop/src/features/sidebar/lib/useSidebarUnreadOverflow.test.mjs**
Covers priority detection and destination-count labels for the new
overflow projection.

**desktop/src/features/sidebar/lib/useSidebarUnreadOverflow.ts**
Counts distinct offscreen unread destinations and determines quiet
versus primary treatment per direction.

**desktop/src/features/sidebar/ui/AppSidebar.tsx**
Renders one stable overflow control, preserves protected-DM visibility
filtering, and prioritizes visible unread DMs for previews and
navigation.

**desktop/src/features/sidebar/ui/AppSidebar.types.ts**
Adds the priority unread destination set to the sidebar contract.

**desktop/src/features/sidebar/ui/CustomChannelSection.tsx**
Stops forwarding non-DM unread counts into custom channel rows.

**desktop/src/features/sidebar/ui/MoreUnreadButton.test.mjs**
Updates control coverage for explicit emphasis, destination labels, and
DM targeting.

**desktop/src/features/sidebar/ui/MoreUnreadButton.tsx**
Applies quiet or primary treatment without changing geometry,
accessibility text, DM avatars, or click behavior.

**desktop/src/features/sidebar/ui/SidebarSection.tsx**
Removes non-DM row numerals while retaining unread weight and thread
preview affordances.

**desktop/src/shared/ui/UnreadPill.tsx**
Shares one composition between quiet and primary states so only color
treatment changes.

**desktop/tests/e2e/badge.spec.ts**
Covers destination counting, promotion without count inflation,
DM/thread priority, row bolding, and removed numerals; also captures the
reviewed UI states.

**desktop/tests/e2e/channel-activity-popover.spec.ts**
Updates channel activity assertions for the numeral-free row treatment.

**desktop/tests/e2e/channels.spec.ts**
Updates channel unread expectations to use bold text rather than a row
count.

**desktop/tests/e2e/thread-unread.spec.ts**
Keeps thread unread-dot and popover coverage while asserting the room
itself is bold.

</details>

## Reproduction steps

1. Open a workspace with enough sidebar destinations to scroll rooms
above or below the viewport.
2. Receive ordinary unread activity in an offscreen room; verify one
quiet `N unread` indicator appears and counts the room once regardless
of message volume.
3. Receive a mention, broadcast, or relevant thread reply in an
offscreen room; verify the same count becomes primary without changing
its geometry or adding a badge.
4. Receive an unread DM, including thread-only activity; verify the
indicator is primary, shows the DM avatar when available, and navigates
to that DM.
5. Scroll the destination onscreen or mark it read; verify the count and
treatment update from the remaining offscreen destinations.
6. Inspect unread non-DM rows; verify their names are bold, numeric
badges are absent, and thread dots still open the unread-thread preview.

## Screenshots

### Ordinary unread room
Routine offscreen room activity uses the quiet treatment.

![Quiet unread overflow
indicator](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/7134/sidebar-unread-overflow-default.png)

### Directed unread activity
The same destination count becomes primary when an offscreen room has
directed activity; geometry and label remain unchanged.

![Primary unread overflow
indicator](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/7134/sidebar-unread-overflow-primary.png)

### Thread-only unread DM
A DM stays primary and retains its avatar even when only its thread has
unread activity.

![Primary DM thread overflow
indicator](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/7134/sidebar-dm-thread-overflow-primary.png)

---------

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Rizz <302abe414ca6e3134763d2539bfcf145aea2a63fe5f8455204ed602fd40cf381@buzz.block.builderlab.xyz>
Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
2026-09-04 23:03:41 +00:00
Salman Mohammed
4beffef697 feat(buzz-acp): update base prompt; add buzz context and skills to Pi agents (#7335)
## Summary

- launch Pi through a private Buzz wrapper so managed Pi agents receive
Buzz's base prompt plus the configured thread/channel session model via
`--system-prompt`
- load the Buzz workspace skill directory with Pi's repeatable `--skill`
flag, making `~/.buzz/.agents/skills/buzz-cli` available alongside Pi's
normal global skill discovery
- report Pi and `pi-acp` installation states separately so setup
guidance points to the missing component

`pi-acp` does not currently consume the ACP `session/new` system prompt,
but its normal new-session and restore paths do honor
`PI_ACP_PI_COMMAND` as the executable used to launch Pi. Buzz reserves
that variable and sets it to a private generated launcher. The launcher
invokes `pi` from Buzz's effective `PATH`, adds `--system-prompt <file>`
and `--skill <workspace>/.agents/skills`, and forwards `pi-acp`'s RPC
and session arguments unchanged. This keeps the integration entirely in
`block/buzz`, without changes to Pi or `pi-acp`.

### How PI_ACP_PI_COMMAND works in `pi-acp`

`PI_ACP_PI_COMMAND` selects one executable; it does not accept
arguments. Therefore:

```bash
# Does not work
PI_ACP_PI_COMMAND="pi --skill ~/.buzz/.agents/skills"
```

Buzz does not expose this variable as user configuration. It creates a
private launcher and sets `PI_ACP_PI_COMMAND` only on the `pi-acp`
child. Conceptually, that launcher executes:

```sh
#!/bin/sh
exec pi \
  --system-prompt "$PRIVATE_SYSTEM_PROMPT" \
  --skill "$HOME/.buzz/.agents/skills" \
  "$@"
```

### Related issue

Follow-up to #7208. No duplicate issue or open PR found.

### Testing

- `just ci`
- `cargo test -p buzz-acp pi_launcher`
- `cargo test --manifest-path desktop/src-tauri/Cargo.toml
managed_agents::env_vars::tests`
- `cargo test --manifest-path desktop/src-tauri/Cargo.toml
managed_agents::discovery::presets::tests`
- pre-push branch checks

No screenshots: the UI change is state-dependent setup guidance only.

---
**Update Sep 4, 13:35:** Made `PI_ACP_PI_COMMAND` entirely Buzz-owned.
- Managed agent configuration now rejects the variable as a user
override.
- Buzz always launches `pi` from its effective `PATH` and rejects
inherited values before creating its private launcher.
- Removed the custom Pi executable discovery path and its internal
environment alias.

---
**Update Sep 4, 14:03:** Reject inherited `PI_ACP_PI_COMMAND` values
instead of replacing them.
- Pi startup now fails with an actionable message telling the user to
unset the variable.
- Removed the Pi-specific exception from the generic ACP environment
injection path.

Generated with Codex

---------

Signed-off-by: Salman Mohammed <smohammed@squareup.com>
2026-09-04 21:17:00 +00:00
Logan Johnson
4afef8649a fix(desktop): restore mention chip identity icons (#7338)
## Summary

Restore the missing **@ glyph for people and robot icon for agents**
after #7133, and incorporate the requested compact public-key display.

- Wrapping mention chips render their existing bounded icon-bearing
leading fragment.
- Readonly chips show bound keys using the same `8 leading…4 trailing`
formatter as the channel member list: `Scout (150b20bd…15dc)`.
- Full literal labels and exact keys remain authoritative in metadata,
profile targets, title/accessible-name attributes, editor text, saved
bodies and recipient tags. Display abbreviations are never used for
recipient lookup.
- Copy/paste restores the full literal label for a complete compact
chip; partial selections remain plain text. Two keys sharing the same
abbreviation still round-trip to their separate exact recipients.
- No recipient-resolution, authorization, wire-format, composer, or CSS
changes. Existing labels, icons, cloud markers and ordinary mentions
remain intact.

## Verification

Published candidate `9365ab9bc9d9c5d10802580cd576f8e378ff492f`, based on
main `e09f715c9d0ee2cb7bf8a39061e601f3a502f588`:

- **6,444 desktop unit tests passed** on this candidate's final source
tree.
- **44 mock-Chromium tests passed, zero retries in the final run**
across mention recipients, clipboard and cloud provenance: exact
recipient selection, ambiguity rejection, send/edit/reopen, forwarding,
full/partial copy, mismatched-key rejection, matching-abbreviation
collisions and 100%/150% narrow-window geometry.
- TypeScript, desktop Biome/check guards, protected-feature production
build and E2E build passed.
- The compact-renderer regression fails with the formatting call
removed. Original missing-icon and hidden-text accessibility regressions
have red/green evidence.
- Fresh self-review traced rendering, full-key metadata, copy
classifier, paste normalization and identity trust. The same display
formatter owns the accepted compact form on both clipboard sides.

Iteration exposed an existing team-insertion separator flake (passed
final full run) and two new fixture assumptions: non-member sends
require invitation, and Chromium rich paste may retain an NBSP
separator. Tests now exercise invitation and normalize only that
separator when comparing the captured full body and exact tags; no
product change was needed for either.

Earlier local repository-wide `just ci` completed in two invocations
because its initial call hit the ten-minute tool limit during Tauri
compilation. Unchanged native/mobile/backend evidence is reused; the
desktop delta received the full checks above and new remote CI. Native
VoiceOver, real Tauri selection, dark theme and non-Chromium observation
were not performed. Browser artifacts exercise real frontend with mocked
Tauri/relay boundaries, not an installed release.

## Review and visual evidence

Current-head CI and automated review must complete after this update;
the old `2997bfb5` green results do not establish this new head.
Required human review remains separate from agent approvals. No
merge/install/restart authorization.

Before/after icon evidence:
https://github.com/block/buzz/pull/7338#issuecomment-5544575866

Updated compact-key screenshots are posted below. The editor
intentionally retains the full literal address; only readonly chip
display is abbreviated.

Origin:
buzz://message?channel=3355d33a-b72a-423a-b064-a58275f9a8af&id=38b3a27e689f5a9604e273d45f4e3122fceba76081e7bcd3bbdbf439524a5a18

---------

Signed-off-by: Logan Johnson <loganj@squareup.com>
Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
2026-09-04 13:36:17 -07:00
morgmart
5d107836c6 Persist video playback speed preference (#7336)
## Summary

- persist the selected desktop video playback speed as a device-level
preference
- apply the shared preference to inline and review video players,
including after a reload
- cover persistence, validation, cross-window updates, and the
end-to-end playback flow

## Verification

- `node --test
desktop/src/shared/lib/videoPlaybackSpeedPreference.test.mjs`
- `cd desktop && pnpm exec tsc --noEmit`
- `cd desktop && pnpm check:px-text`
- `cd desktop && pnpm test`
- `cd desktop && pnpm build:e2e && pnpm exec playwright test
--project=smoke video-attachment.spec.ts`
- pre-push: desktop check, file-size check, typecheck, and 6,416 desktop
tests

---------

Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com>
2026-09-04 13:10:17 -07:00
Logan Johnson
e7e29937a1 feat(desktop): invite owned agents from standalone forums (#7125)
## Summary

In Desktop's standalone Forums, selecting an owned agent from another
device could leave a post or reply unsendable if the agent had not
joined the forum. This adds **Invite / Cancel** to the send flow so you
can resolve membership without leaving your draft.

- **Invite** checks response policy and your permission to add members,
adds the agent to the forum, waits for refreshed membership, then
rechecks authorization before posting to the original destination.
Membership is forum-wide, not limited to one post.
- **Cancel / Escape** keeps the text, attachments and selected
recipients for retry. Unlike chat's **Do nothing / Send anyway**, this
dialog has no reference-only send choice. Invite is disabled while
pending; Cancel remains available.
- Leaving the source post/reply cancels its pending invitation, even if
you return. Errors remain visible, focus returns to the initiating
editor when appropriate, and late completion cannot resume a cancelled
post or interfere with a newer attempt.
- A rejected send restores text, uploaded media and exact selected
recipients to the source draft only if no newer edit, deletion, upload
intent or send supersedes it. Clipboard verification settles before
recipient capture, with stale edits/navigation fenced out.

### Related issue

Targets `main` after #7124 merged. This PR reuses its publication checks
and draft protection; the five forum commits have been replayed
unchanged onto the merged parent. Owned-agent discovery (#7122) is
already merged. Split from #7114.

Forum creation/templates, channel-less Notes and local-agent management
are unchanged. Duplicate-name binding from #7133 is already merged and
retained by this stack. Inviting does not start a remote agent or
promise that it is online or will reply.

### Testing

Forum composer lifecycle tests, including clipboard-settlement cases,
and TypeScript/changed-file formatting checks passed after the restack.
Earlier invitation, focus and transport-recovery browser coverage is
retained, not claimed as a fresh full browser run on this head. See
[live CI](https://github.com/block/buzz/pull/7125/checks) for
current-head results. Browser evidence uses mock IPC; no full local
`just ci` pass or native/live-relay validation is claimed.

To try it: open a forum post or reply, select an owned nonmember agent
and send. Cancel, then retry without reselecting; Invite should add that
agent before posting. Deny the add to check the visible error and
retained draft. Navigate away/back during a pending invitation or
rejected send; no stale publication or overwrite of a newer draft should
occur.

![Forum invitation with Invite and
Cancel](https://raw.githubusercontent.com/block/buzz/6481bd088d661975672addcc834231df6ac88705/pr-7125--forum-invite.png)

*Earlier mock-browser capture, not current-head runtime proof. [Error
and successful-post
captures](https://github.com/block/buzz/pull/7125#issuecomment-5513883462);
no before-state/native capture available.*

**Limits:** cancellation cannot undo accepted membership changes or
dispatched posts; authorization and publication are not atomic. Recovery
is same-window, subject to browser storage limits, and is not a durable
in-flight send journal: reload/crash can lose a pending snapshot.
Cross-window coordination and in-flight upload custody are unchanged.
The parent's legacy member-agent compatibility does not establish
ownership for nonmember invitations.

---------

Signed-off-by: Logan Johnson <loganj@squareup.com>
Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
2026-09-04 17:33:30 +00:00
Logan Johnson
d595806fc3 fix(desktop): authorize remote mentions at publication (#7124)
🤖

## Summary

In Buzz Desktop, you could own an agent running on another device but be
unable to mention it in a channel where it had not yet joined: it was
filtered out before you could invite it. A selected agent could also
disappear from the message's recipients when permissions changed. This
lets you select an eligible agent in the existing **@ menu**, invite it
from the message composer, and send to that agent—or see an error and
keep your draft rather than silently sending without it.

#### Where the experience changes

| Screen / control | Before → after |
| --- | --- |
| A channel's **Message #…** composer, or a message's **Reply in thread
to …** composer | Type `@` (or use the existing @ button), choose your
agent, write the message and press **Send message**. An owned agent not
yet in the channel can now reach the existing **“Mention people outside
this channel?”** dialog when its response settings allow you to address
it. |
| That dialog's **Invite** button | Previously the membership
requirement could block the agent before the invitation. Now Invite
checks permission to add it, adds it as an agent member of the
**channel** (not just the thread), then rechecks membership and response
permission before sending the waiting message. An agent already in the
channel needs no invitation. |
| Existing direct message, or the new-message screen with the **To:**
field | A mention is checked against the conversation the message will
actually enter, including a newly created direct message, rather than
the old or not-yet-created destination. This does not add an Invite
control to direct messages. |
| Editing a message / sending attachments | The selected agent remains
part of the send or edit attempt through attachment upload and the final
permission check. Lost permission produces a visible error instead of
dropping that recipient. |

**Invite is not the only chat choice.** The existing **Do nothing**
button sends the message *without inviting or notifying the nonmembers*;
their names remain references in the text. Where you cannot invite, that
choice is labelled **Send anyway**. To abandon the send instead, dismiss
the dialog with Escape. Invitation actions are disabled while
preparation is pending, preventing duplicate clicks.

**Leaving and returning must not resurrect a cancelled send.** Switching
threads or leaving the composer cancels its pending invitation, even if
you return to the same thread. Cancellation before dispatch sends no
message; an accepted membership change cannot be automatically undone.
An ordinary send without a pending invitation remains bound to its
original destination rather than following you into another
conversation.

**Failed sends must not overwrite your next draft.** If you leave a
thread, return and replace or deliberately clear its draft while an
older send is pending, the older failure cannot restore deleted text,
recipients or files; success cannot erase the newer draft—even if its
text is identical. An untouched draft cleared automatically for sending
remains recoverable on failure. This protection also covers reopening
the composer and starting a newer send.

The channel timeline also keeps its existing **new-messages / Jump to
latest** button available when newer messages are waiting to be
displayed. For example, after sharing a reply to the channel and closing
the thread panel, you can click the catch-up button to reveal buffered
messages. Closing the thread does **not** guarantee the shared row
appears automatically or force you away from reading history.

### Related issue

Built on [#7122](https://github.com/block/buzz/pull/7122), base branch
`split/owned-agent-discovery`, which lets Desktop find and verify owned
agents independently of this device. Current integration head:
`1144465d00273cf74b7c22544ae5a3299bd98560`, built on exact published
root `3a56d17824522580fe04cae463b54f4c7ba66021`. Root #7122 has its own
CI and security gates; this PR must not land ahead of that dependency.
Finding an agent is not channel membership, online status or a promise
of a reply. This PR changes what the existing message controls can do
with those agents; it adds no profile, presence, cloud marker or remote
start/stop UI.

Standalone forum post/reply **Invite / Cancel** is added separately in
[#7125](https://github.com/block/buzz/pull/7125); here those composers
only gain visible authorization errors. Same-name selection/binding
fixes ([#7133](https://github.com/block/buzz/pull/7133)) and mention
spacing ([#7128](https://github.com/block/buzz/pull/7128)) are not
included.

Extracted from [#7114](https://github.com/block/buzz/pull/7114)
(historical source `98fe33ec`). [Behavior and draft-recovery
contract](1144465d00/docs/remote-mention-routing.md)
· [Originating
discussion](buzz://message?channel=f7a9536a-1738-4bad-a888-b3ea25010ef1&id=7aa1f0ab23dce514bd8a0221441cf005bf428914621171472b79747c50820848).

### Testing

![Channel composer after Send: RemoteScout is not a channel member;
Invite adds it before sending, while Do nothing sends without inviting
or notifying
it](https://raw.githubusercontent.com/block/buzz/b20345da0bd648ecbc78988637c89893a33ca2f3/pr-7124--remote-invite.png)

*Earlier candidate, mock desktop browser: the existing channel dialog
now reachable for an eligible owned agent on another device. The two
buttons have different send outcomes; Do nothing is not Cancel.*
[Success and denial
captures](https://github.com/block/buzz/pull/7124#issuecomment-5482292703)
· [Pending-state
capture](https://github.com/block/buzz/pull/7124#issuecomment-5482604329).
These show the relevant UI, not live agent availability, native
authorization or the later draft-storage/catch-up repairs. No
before-state screenshot is available.

Existing coverage exercises exact recipients, invitation
rejection/cancellation, new direct-message destinations, uploads, edits,
thread re-entry and stored-draft deletion. The timeline regression
checks the shared reply becomes visible using the available catch-up
action.

**Integration validation (2026-09-02):** independently reviewed the
routing delta onto root `3a56d178`: seven original patches unchanged;
two reconciliations retain generic publication-error toasts alongside
authorization errors and retain non-authored editability updates. Added
two production-hook regression tests (normal and queued-media
publication) requiring visible generic error, recovered draft and
released pending state.

- Writer validation: **5,995 Desktop tests**, **42 focused tests**, **22
mock-IPC browser journeys** (18 routing, 2 root provenance, 2
destination binding), and **1 voice-note failure journey** passed; lint,
types, size guards and E2E build also passed.
- The broad suite ran before the final formatting-only test amendment,
not as an exact-final-head rerun. Independent AST comparison confirmed
that amendment is semantics-preserving; **4 fresh assertions at final
`1144465d`** passed. Publication rechecked final-head TypeScript,
amended-test formatting and `git diff --check` successfully. No new full
repository `just ci` run is claimed.
- Browser tests use an isolated E2E build and **mock IPC**, not live
relay/native authorization. Historical screenshots above are explicitly
earlier UI evidence, not exact-head runtime certification. Packaged
Tauri/live-relay behavior was not independently witnessed.
- **Published-head gates:** [current CI
run](https://github.com/block/buzz/actions/runs/33657948560) and
[renewed exact-head formal review
request](https://github.com/block/buzz/pull/7124#issuecomment-5513218340)
must clear before landing. [Earlier CI
run](https://github.com/block/buzz/actions/runs/33438436438) and the two
earlier approvals cover `7ffead0f`, not this new head. Root CI/security
clearance remains separate; the independent scoped integration approval
is not merge authorization.

To try it: in a channel or thread, select an owned nonmember agent,
Send, then Invite or Escape and retry. Deny the add or revoke its
response permission before sending: expect a visible error and
recoverable draft, not a message missing the agent. During a pending
send, return to the source thread, edit or clear the draft, then leave
again: late completion must not overwrite that choice.

**Limits:** permission checks and sending are separate operations;
cancellation cannot retract a dispatched message. Draft protection is
same-window, not new cross-window deletion synchronization. Standalone
forum transport failure can still restore text/media without the exact
selected recipients. Native compatibility is inherited: open-source
builds may still recognize a valid legacy, self-declared agent already
in the channel when verified ownership is absent or rejected; that does
not establish ownership or unlock this owned-nonmember invitation path.
Invalid policy from a verified owner is still rejected. No agent
response is guaranteed.

---------

Signed-off-by: Logan Johnson <loganj@squareup.com>
Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
2026-09-04 16:35:35 +00:00
Salman Mohammed
ce9decb235 fix(acp): rename system tag to agent-instructions (#7332)
## Summary

Rename the paired persona prompt boundary from `<system>` to
`<agent-instructions>` in both modern and legacy ACP delivery paths.
Update Desktop diagnostics to label the new boundary as Agent
Instructions while keeping archived `<system>` captures readable.

`system` is confusing (esp with system role) and agent instructions is
what shows up in the UI today

<img width="767" height="298" alt="Screenshot 2026-09-04 at 11 37 54 AM"
src="https://github.com/user-attachments/assets/4e34ac06-db50-461e-a555-ce17885b0031"
/>

### Related issue

Follow-up to #6701. No duplicate open issue or PR found.

### Testing

- `cargo test -p buzz-acp`
- `cargo clippy -p buzz-acp --all-targets -- -D warnings`
- `node --import ./desktop/test-loader.mjs --experimental-strip-types
--test
desktop/src/features/agents/ui/agentSessionTranscriptHelpers.test.mjs
desktop/src/features/agents/ui/agentSessionTranscript.test.mjs`
- `pnpm --dir desktop typecheck`
- Pre-push hook: 6,237 Desktop tests, 3,164 Tauri tests, and all 13 Rust
unit-test lanes passed

Generated with Codex

Signed-off-by: Salman Mohammed <smohammed@squareup.com>
2026-09-04 16:35:18 +00:00