Commit Graph

155 Commits

Author SHA1 Message Date
Salman Mohammed
78618804ec Configure ACP session scope per agent (#7578)
## Summary

PR #6732 taught the ACP harness to honor `BUZZ_ACP_SESSION_POLICY`, but
the desktop exposed that policy as one global experiment. Turning it on
changed every managed agent at once, even though conversation scope is
part of how an individual agent should behave. Some agents need
continuity across a channel, while agents such as validators may need
clean context for each thread.

This moves the choice onto the agent definition under Advanced as
**Conversation context**, with **Entire channel** and **Each thread**
options. **Entire channel** remains the default, so existing definitions
keep their current behavior.

The policy now travels through create and update IPC, returned agent
summaries, persona events, snapshots, teams, and community catalog
copies. Launches pass the effective definition value to the harness.
Editing a running agent uses the same lifecycle as a model edit: it
shows the restart-required state, leaves the deployed process untouched,
and applies the new policy on restart.

The global Thread Scoped ACP Sessions experiment and its desktop
command/state have been removed because the setting no longer has a
useful global meaning. There is intentionally no migration for the old
preview override. Users who enabled it can choose **Each thread** for
the agents that need it.

Unknown or null policy values degrade to **Entire channel** across
stored agents, persona events, and catalog projections. This keeps
forward-version data from dropping complete agent records.

### Related issue

No matching issue found. Builds on #6732.

### Testing

- Focused Playwright create flow selected **Each thread** and verified
`create_persona` received `sessionPolicy: "thread"`.
- Focused Playwright catalog flow added a thread-scoped agent and
verified the local copy stored `session_policy: "thread"`.

Generated with Codex

---------

Signed-off-by: Salman Mohammed <smohammed@squareup.com>
2026-09-11 20:38:41 +00:00
Salman Mohammed
813bbd1412 fix(acp): integrate the Buzz Pi adapter fork (#7552)
## Summary

PR #7335 worked around missing Pi adapter support by generating a
private Pi launcher and injecting Buzz's standing prompt and skills at
process launch. The Buzz Pi fork now carries the required adapter
extensions, so this removes that launcher and returns prompt
construction to the normal ACP session path while retaining the
base-prompt composition introduced by #7335.

The Pi preset now installs `salman1993/pi-acp` and launches its renamed
`buzz-pi-acp` binary. Buzz adds `-- --skill
<harness-cwd>/.agents/skills` only when launching that binary, sends the
complete composed prompt as the `_meta.systemPrompt` replacement string
on `session/new` only when `initialize.agentInfo.name` is `buzz-pi-acp`,
and sends the scoped title alongside it as `_meta.sessionTitle`. The
fork identity is treated as system-prompt capable regardless of its
reported ACP protocol version, which prevents duplicate legacy
user-message framing. Upstream `pi-acp` does not receive either
fork-specific behavior. Observer transcript projection accepts the
string, `{ replace }`, and `{ append }` metadata forms.

The fork now stores restore metadata in one atomic file per session
under `~/.pi/buzz-pi-acp/sessions/`. This prevents concurrent Buzz
workers from overwriting another session's prompt or title. The fix
landed in
[salman1993/pi-acp#9](https://github.com/salman1993/pi-acp/pull/9).

This supersedes the closed #7508. No agent-configuration rules changed;
this changes the Buzz Pi adapter contract and launch arguments.

### Related issue

#7329

### Testing

Installed fork commit `09cf07e436b8f18e52401558f988f31a15702313` through
the documented Git URL. The installed bundle matched the committed
`dist/index.js` byte for byte and contained the `~/.pi/buzz-pi-acp`
metadata path. The fork's 106 non-skipped tests, typecheck, and lint
pass.

Ran the ignored real-Pi integration test through Buzz's production
session composer. The test exercised the renamed package,
`agentInfo.name`, and the new per-session metadata store. Base, persona,
team, core-memory, huddle, canvas, and skill markers each appeared once
after switching sessions and again after restarting the adapter, while
the other session and Pi's native default prompt were absent.

Added regression coverage proving `buzz-pi-acp` receives fork-specific
system-prompt metadata and managed skills while upstream `pi-acp` does
not. `just ci` passes.

Generated with Codex

---------

Signed-off-by: Salman Mohammed <smohammed@squareup.com>
2026-09-10 21:38:42 -04:00
Logan Johnson
2226b6f95b fix(desktop): npub identity controls across profile, agents, and workflows (#7489)
🤖
## Summary

Building on #7488's npub foundation, this PR finishes the identity
display change for the controls where you actually manage people and
keys: profile, settings, agent access, and workflows. Everywhere in
these surfaces, an identity key shows — and copies — as its canonical
npub (npub is the human-readable encoding of a Nostr public key: the
compact `npub1j57...fjmv` form where space is tight, the full npub where
the whole key matters), and accepts npub as input.

After this change:

- Profile panel: the public-key row and the managed-by / declared-owner
copies show the full npub. If a key can't be encoded, you see
"Unavailable" with no copy button — never a raw or partial key.
- Settings: the identity card shows and copies the npub. The
hosted-communities account identity derives from the bound key
(`pubkey_hex`) — the same authority as the mismatch gate and hosted
operations — so the display can never disagree with what the app acts
on; an unusable hex falls back to a neutral label instead of rendering
the unverified server npub. The connected claim and a community's
Connect action require that same usable bound key to match the local one
— with no usable binding the card cannot claim connected or start
Connect, while the community list, linking, and delete/rebind recovery
stay available.
- Hosted create/onboarding: the account and device identity rows in the
create flow and owner onboarding derive from the same authoritative
fields (bound key / local key), with the same neutral fallback;
readiness requires a usable bound key that matches the local one.
- Respond-to allowlist (controls who may respond to an agent): entries
can be typed or pasted as hex or npub; both spellings of the same key
are recognized as one entry and dedupe. Search results, chips, and
remove buttons use the compact npub.
- Workflow key fields: to/from keys display as npubs in the form and
save back as canonical hex. Templates like `{{trigger.author}}`, roles,
and free text pass through untouched; placeholders accept both
spellings.
- Recipient and agent dialogs: the verify popover is npub-only (the
raw-hex line is gone); denied-membership screens never show a raw key.
- The Rust-side truncated display name (used for native surfaces) shows
the same compact npub, so those surfaces match the web UI.

Internal representation is unchanged: keys are still stored, sent, and
matched as canonical 64-character hex — npub is a display and input
spelling, normalized to hex at the boundary, so existing data and
integrations keep working. Bound-key usability and comparison use one
normalized form (trimmed, lowercased, 64 hex characters; npub rejected),
so padded or mixed-case spellings of the same key match. Display names,
private keys, and event IDs are untouched.

## Details

- `respondToAllowlist` / `RespondToField`: npub entries normalize to
canonical hex; cross-form dedupe; compact npub in rows and chips;
direct-add accepts npub and stores canonical hex.
- `workflowFormTypes` / `WorkflowStepCard`: hex → npub for display, npub
→ canonical hex on save; templates, roles, and free text pass through in
both directions (roundtrip-tested).
- `UserProfilePanelFields`, `ProfileSettingsCard`,
`HostedCommunitiesSettingsCard`, `MembershipDenied`,
`SelectedRecipientChip`, `AddAgentToChannelDialog`: npub display and
copy; invalid keys → "Unavailable" with no copy; hosted identity rows
derive from the bound `pubkey_hex` (create/onboarding rows from the
bound and local keys), never the unverified server npub;
connected/readiness/Connect gates use the same usable-bound-key
predicate, and the settings Connect invocation callback re-checks it
before starting.
- `src-tauri/src/commands/identity.rs`: `truncated_display_name`
compacts to the first 8 + last 4 characters of the npub (above a 12-char
threshold), mirroring `truncateNpub`.
- e2e: profile key rows and clipboard polls assert npub forms and
raw-hex suppression; the display-screenshots spec pins the npub-only
popover; hosted specs drive the real settings card, create flow, and
onboarding rows through their real providers, and the unlinked/npub-only
identity cases assert no connected claim and no Connect action.

### Related issue

- Fixes: N/A. No separate issue; the related work is the stack below.
- Stack: builds on #7488 (shared npub foundation), now merged; this PR
is rebased onto main and stands on its own.

### Testing

At head `303c90ffa` (base: main `bfc38485`; 24 files, +1125/−146):

- Focused unit suites (respondToAllowlist, workflowFormTypes,
hostedCommunityApi bound-key helpers) green; mutation-checked — dropping
allowlist canonicalization fails the dedupe case, and dropping bound-key
normalization fails the npub-in-hex and padded same-key cases.
- Full desktop unit suite 6,477/6,477, `desktop-typecheck`,
`desktop-check` (formatting fixed narrowly with `biome check --write` on
the touched files only), and a fresh E2E build at the current head; the
add-community + hosted-communities-settings specs 18/18 and onboarding
integration 69/69 on a fresh dedicated port, with focused new-case runs
4+4 covering padded same-key (ready, Connect kept — no false rebind) and
npub-in-hex (neutral label, recovery, no Connect) across the settings
card, create flow, and first-community onboarding, plus the
unlinked-account settings regression asserting Connect cannot occur.
- `cargo fmt`/clippy (both feature sets) and `cargo test identity` (71
pass) passed at the earlier full-change head; since then, the only
production changes in this PR's delta are the hosted identity display
authority and its fail-closed bound-key gating/normalization above
(base-side fixes carry #7488's receipts) — every other change is
test-only.

### Task provenance

Buzz channel: `1f0e4a3d-7e01-4efe-bb16-843b357f85c9`

Task:
buzz://message?channel=1f0e4a3d-7e01-4efe-bb16-843b357f85c9&id=86b34eb4bd84a1472419e9af22636c011c0fe273e3c196f967d7a36996e149b6

---------

Signed-off-by: Logan Johnson <loganj@squareup.com>
Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
2026-09-09 14:08:01 -04:00
Logan Johnson
ad2a84131e fix(desktop): npub identity displays for mention, member, and workflow surfaces (#7495)
🤖
## Summary

Every Buzz account is identified by a long public key. Before this
change, when someone had no display name, surfaces fell back to
inconsistent labels — mostly raw hex fragments like `abcd1234…wxyz`,
sometimes a generic role label with no key — so the same person looked
different from surface to surface, and nothing looked like an npub
address. This PR applies the npub identity foundation from #7488 to the
everyday surfaces: a person without a display name now falls back to the
same compact npub everywhere — `npub1xxxx…yyyy`, the human-readable
spelling of their public key (first 8 + last 4 characters of the full
npub) — across messages and mentions, reactions, huddles, member and
participant lists, the sidebar and channel activity, search, projects,
tray, notifications, and workflow surfaces.

- **Mentions and messages**: key-only mention chips render the compact
npub. Pasting a copied mention back still re-binds it byte-exactly to
the identity it declares, for both the new npub chips and legacy
hex-truncated chips copied by older clients — wrong, missing, or
tampered key qualification is rejected instead of silently degrading to
plain text.
- **Reactions and huddles**: huddle reaction events and the huddle
roster/participants render the compact npub for unnamed participants;
workflow reaction triggers describe authors with the same form.
- **Members and sidebar**: channel and community member lists,
add-member results and invites, the members sidebar, the
channel-activity popover, search, projects (assignees/reviewers/PR
panels), the tray menu, and desktop notifications all fall back to the
compact npub; titles and aria labels keep the machine-readable full
labels.
- **Profile labels**: panel/popover display names and owner handles fall
back to the compact npub (never raw hex) when there is no name;
linked-event (nevent) message metadata shows the npub-shaped author
fallback while the event lookup and event IDs are unchanged.
- **Workflows**: author-picker secondary labels, step destination keys,
and trigger-author references render compact npubs; event and blob IDs
keep their existing hex compacts (they are not identities).
- **Avatars stay distinct**: fallback avatars for key-only identities
derive initials from the key's tail, so prefixed role labels like
"Participant npub1…" no longer collapse every unnamed participant onto
the same initials; people with names keep their name initials.

Preserved exactly: display names and distinct avatars, internal hex keys
(storage/API forms unchanged), clipboard identity roundtrips, event/blob
ID compaction, private keys (no nsec path is touched), and nevent link
handling.

Scope: this PR changes what identity labels **display**, not identity
controls — profile/settings copy controls, the respond-to allowlist,
workflow key fields, and agent dialogs are the sibling slice #7489, and
the shared primitives (`canonicalNpub`, `truncateNpub`, the `<PubKey>`
gate, strict input parsing) come from the foundation #7488.

### Related issue

- Fixes: N/A. Searched existing issues/PRs for duplicates — none found;
the related work is the npub identity stack this slice belongs to.
- Base/dependency: stacks on #7488 (foundation) — this PR does not stand
alone on main.
- #7489 is a sibling slice on the same #7488 base
(profile/agent/workflow controls), not a dependency: this PR does not
require #7489, and #7489 does not require this PR — both only require
#7488.

### Testing

At exact head `4763cbeae1dd521309755e6d61f657324cb98667` (base:
`fix/desktop-npub-identity-d1a` @
`5f3a4a8111998c8aa41ad77cf66992bd1c85343c`; 71 files, +656/−189 —
production +277/−136, test support +379/−53):

- At this head: targeted `mentions.spec.ts` (1/1), the e2e build,
typecheck, and biome — green.
- 9 changed/related unit files: 100/100 green; typecheck, e2e build,
biome, and px text/truncation checks clean; huddle-roster focused run
green; channel-activity e2e 11/11; mutation checks confirm the fallback
wiring (removing it collapses shared initials and drops fallback rows).
- Known pre-existing local e2e failures, unchanged by this PR and
reproduced identically at the upstream merge-base: huddle-transcription
voice-menu attribution (25 pass / 1 fail) and the
`workflow-local-controls` 438px caret drift. Not claimed green locally.
- Update at head `236af9e6137386737e84d3a474d6bc808a704c50` (test-only
follow-ups `1143af345` + `236af9e6`): the `workflow-local-controls`
races were fixed in the test drivers, and the 438px diff was shown to be
a stale Darwin snapshot baseline (name-row enable switch already absent
and `message_posted` already MessageSquare at recording commit
`9390e11c9`) and refreshed — the focused screenshot test, including
keyboard/caret assertions, now passes locally (twice). The full spec was
not rerun after the snapshot refresh; the huddle-transcription item
above is unchanged.

Label/copy text changes are asserted by the e2e specs (`mentions`,
`mention-recipients`, `pubkey-display-screenshots`,
`huddle-transcription`, `channel-activity-popover`,
`workflow-local-controls`) rather than new screenshots; the screenshot
spec pins the compact npub text forms.

### Task provenance

Buzz channel: `1f0e4a3d-7e01-4efe-bb16-843b357f85c9`

Task:
buzz://message?channel=1f0e4a3d-7e01-4efe-bb16-843b357f85c9&id=86b34eb4bd84a1472419e9af22636c011c0fe273e3c196f967d7a36996e149b6

---------

Signed-off-by: Logan Johnson <loganj@squareup.com>
Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
2026-09-09 14:07:24 -04:00
Taylor Ho
1c8321cd08 fix(desktop): retain automatic mentions only in threads (#7144)
**Category:** fix
**User Impact:** Top-level channel messages now notify only agents
explicitly selected for that message, while thread replies visibly
retain their addressed agents.

**Problem:** Channel-root and thread composers presented the same
automatic-mention model even though retained recipients are only
predictable within an ongoing thread. That could make a new top-level
message notify an agent the sender did not deliberately choose for that
message.

**Solution:** Make retained audiences a thread-only capability. Root
messages remain explicit and one-shot; threads retain visible, removable
agent recipients, with the automatic-mention setting exposed directly in
the mention picker.

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

**desktop/src/features/channels/ui/ChannelPane.tsx**
Removes persistent audience state from the channel-root composer.

**desktop/src/features/messages/ui/ComposerAddressControls.tsx**
Uses the broader **Manage mentions** label because the picker includes
people as well as automatic agent controls.

**desktop/src/features/messages/ui/ComposerAddressControls.test.mjs**
Locks the updated accessible label and active treatment.

**desktop/src/features/messages/ui/MentionAutocomplete.tsx**
Shows the right-aligned automatic-mention setting directly, uses
thread-specific copy, preserves keyboard/focus behavior, and keeps the
current mention when retention is unchecked.

**desktop/src/features/messages/ui/MentionAutocomplete.test.mjs**
Covers the always-visible setting, compact layout, copy, and
thread-scoped agent actions.

**desktop/src/features/messages/ui/MessageComposer.tsx**
Separates unpinning an agent for future replies from removing its
current draft mention.

**desktop/src/features/messages/ui/MessageComposer.types.ts**
Narrows retained audience contexts to threads.


**desktop/src/features/messages/ui/persistentAgentAudienceHosts.test.mjs**
Prevents channel-root and new-message hosts from opting back into
retained audiences.

**desktop/src/features/messages/ui/useAgentAddressLockPicker.ts**
Splits unpin and current-mention removal semantics.

**desktop/src/features/messages/ui/useAgentAddressLockPicker.test.mjs**
Verifies unpinning retains the current draft mention.

**desktop/src/features/settings/ui/AgentsSettingsPanel.tsx**
Describes the preference as addressing selected agents in thread
replies.

**desktop/tests/e2e/persistent-agent-audience.spec.ts**
Moves retained-audience lifecycle coverage to thread composers and adds
root, settings, layout, focus, keyboard, unpin, and draft regressions.

**desktop/src/features/messages/ui/MessageComposerAutocompletes.tsx**
Preserves composer focus ownership while routing the thread-only
controls.

**desktop/src/features/messages/ui/useComposerFocusOwnership.ts**
Keeps focus within the composer while interacting with its mention
overlay controls.

</details>

### Reproduction steps

1. Enable **Automatically mention agents** under agent settings.
2. In a channel root, select an agent and send a message. Confirm the
agent is addressed once, no retained-recipient control appears, and the
next root message has no agent recipient.
3. Open a thread and select an agent. Confirm the visible recipient
persists into later replies.
4. Open **Manage mentions** in the thread composer. Confirm the
automatic-mention setting is immediately visible, right-aligned, and
labeled **Address selected agents in thread replies**.
5. Uncheck a selected agent. Confirm its current draft mention remains,
while later replies no longer retain it automatically.

---------

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-01 16:07:35 -07:00
Salman Mohammed
42aeb1571c feat(desktop): add Pi agent preset (#7208)
## Summary

Add Pi as a selectable desktop ACP runtime via `pi-acp`, including
discovery guidance and its official logo.

### Related issue

Follow-up to closed #6546.

### Testing

- Focused Tauri preset and desktop logo tests
- TypeScript typecheck

Generated with Codex

---------

Signed-off-by: Salman Mohammed <smohammed@squareup.com>
2026-09-01 17:41:09 -04:00
Salman Mohammed
59328d5ae3 feat(desktop): add thread-scoped ACP session experiment (#6909)
## Why

Thread-scoped ACP sessions from #6732 need an opt-in desktop rollout
that preserves today’s channel policy by default.

## What

- Add the default-off “Thread Scoped ACP Sessions” toggle under Settings
→ Experiments using the existing feature-override persistence.
- Map the persisted setting to `BUZZ_ACP_SESSION_POLICY=channel|thread`
for every local and provider-backed managed ACP launch. Changes apply
when managed agents next start; DMs remain conversation-scoped by the
backend.
- Keep the desktop setting authoritative over descriptor environment and
cover the UI, persistence, local spawn, provider payload, and existing
Projects/Workflows experiments.

## Risk Assessment

Low. The experiment defaults off and explicitly preserves `channel`;
changes are limited to managed-agent launch configuration. Existing
running agents are unchanged until their next start.

## Testing

- `. ./bin/activate-hermit && just ci` — passed on the final restacked
tree, including 5,674 desktop tests, 2,796 Tauri tests, and 1,860 mobile
tests.
- `cd desktop && pnpm build:e2e && pnpm exec playwright test
tests/e2e/experimental-features.spec.ts --project=smoke` — 1 passed.
- `. ./bin/activate-hermit && cargo test --manifest-path
desktop/src-tauri/Cargo.toml session_policy --lib` — 4 passed.
- `. ./bin/activate-hermit && cargo test --manifest-path
desktop/src-tauri/Cargo.toml commands::agents::deploy::tests --lib` — 15
passed.
- `. ./bin/activate-hermit && cargo test -p buzz-backend-kubernetes
--test wire_fixtures` — 4 passed.

## Stack Info

Stacked on #6732. This PR depends on #6732 and should merge after it.

Generated with Codex

---------

Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Signed-off-by: Leo <5faf251baee50ee6bcde338aef6acdd70bb3e60115664c2cd490d94a55dfc488@buzz.block.builderlab.xyz>
Co-authored-by: Leo <5faf251baee50ee6bcde338aef6acdd70bb3e60115664c2cd490d94a55dfc488@buzz.block.builderlab.xyz>
2026-09-01 08:37:27 -05:00
Arjun Mahanti
5673c3307e feat(desktop): add protected-build Bestie experiment (#6902)
## Summary

Introduces a protected-build boundary for the default-off Bestie
experiment without adding any Bestie product surface.

- Official OSS builds select an empty protected-feature module and emit
no Bestie/Chief metadata or implementation content.
- Protected internal builds select a separate module graph containing
the Bestie experiment definition.
- Within an internal build, Bestie remains disabled until the user opts
in under Settings → Experiments.
- The production build runs an artifact matrix and fails if OSS output
contains protected content or internal output lacks the Bestie manifest.

## Build contract

| Build variant | User opt-in | Result |
| --- | --- | --- |
| Official OSS | Any/forged | Bestie absent from the compiled artifact |
| Protected internal | Off | Bestie available but disabled |
| Protected internal | On | Bestie enabled |

The companion protected-release change is squareup/buzz-releases#91. It
sets `VITE_BUZZ_BESTIE=1`, requires that exact value, forwards it into
the signed macOS build, and asserts the contract in release validation.

## Why this is separate

This gives later Bestie PRs one build-selected import seam. Protected
implementations must be reachable only from the internal module so they
never enter the official OSS module graph.

## Non-goals

- No Bestie persona or provisioning
- No sidebar, app-chrome, or message-toolbar UI
- No entitlement or secrecy claim: the source is public; this boundary
controls official Block artifacts

## Verification

- Exact commit `523cf49ced03cba9be43836a54d6aa5d6923cc82`
- Full `just ci`: 5,673 Desktop tests, 2,773 Tauri tests, 1,860 mobile
tests, Rust/Tauri/web/mobile static checks and builds
- OSS production artifact: scanner confirms no `Bestie`, `Chief of
Staff`, or `builtin:bestie` content
- Internal production artifact: scanner confirms the protected Bestie
manifest is emitted
- Both build orders verified; `dist` retains the requested variant for
Vite/Tauri packaging

---------

Signed-off-by: Arjun Mahanti <arjun@squareup.com>
Signed-off-by: Fizz <fizz@buzz.local>
Signed-off-by: Fizz <dae5f6af70b8695a8b83c8deae555f63be41630ec2b8cd493e41a439c9527dd8@buzz.block.builderlab.xyz>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Fizz <fizz@buzz.local>
Co-authored-by: Fizz <dae5f6af70b8695a8b83c8deae555f63be41630ec2b8cd493e41a439c9527dd8@buzz.block.builderlab.xyz>
2026-08-31 15:03:35 -06:00
Will Pfleger
2f66ee24a0 feat(desktop): add team sharing to community catalog (#3995)
Stack: [#5112](https://github.com/block/buzz/pull/5112) → this PR

Stacks on the team catalog backend PR (#5112). Contains the `desktop/**`
changes that turn the add-agent surface into a single Community Catalog
browsing both shared agents and shared teams.

## Owner catalog sync pipeline

`usePersonaSync.ts` gains a hydration pipeline in `startPersonaSync` so
a fresh device that comes online after another already published the
owner's 30178 catalog head reconstructs the complete state without
falsely retracting it. The owner's
persona/team/managed-agent/30178/deletion history is backfilled up front
(a live-only subscription gets no history — reconnect-replay's
since-cursor is undefined until the first live event), then a live
subscription takes over.

Four properties keep a fresh sync from purging the owner's valid shared
head:

- **Paged backfill with a safe termination guarantee.** The relay serves
each REQ newest-first and clamps `limit` to its advertised `max_limit`,
so a large owner's history overflows one page — a newer 30178/30176
could return in-page while an older required 30175 constituent falls
beyond it. `fetchOwnerHistoryToExhaustion` pages the full window with
the `until` time cursor (the only cursor the WS REQ filter exposes; the
DB `before_id` keyset is REST-only), deduping the inclusive-boundary
rows. A short page terminates normally. A full page whose oldest event
cannot advance the time-only cursor is a **dense boundary** — more than
one page of events share one `created_at` second, which the WS filter
has no `(created_at, id)` cursor to escape — and raises
`PersonaHistoryDenseBoundaryError` rather than silently completing as if
the history were exhausted and dropping the older constituents behind
it.
- **Constituents-before-catalog ordering.** Over the complete batch,
`orderCatalogHeadsLast` stably defers every 30178 head past its
30175/30176 constituents. Reconciling a 30178 head before its personas
hydrate makes the inbound team refresh fail member resolution and queue
a dominating false tombstone; deferring the heads guarantees the
constituents are all applied first, while newest-wins order within every
other coordinate is untouched.
- **Hydration boundary for concurrent live events.** The backfill fetch
and the live subscription start concurrently into one reconcile chain. A
live or replayed 30178 that arrives before the backfill reconciles its
constituents would reproduce the same false-tombstone purge, so live
events are buffered until the ordered backfill is dispatched and then
drained in arrival order. Steady-state live events (after hydration
completes) reconcile immediately.
- **Explicit backfill failure policy.** A transient history-fetch
rejection is retried with bounded backoff. When backfill cannot complete
— retries exhausted, or a deterministic dense boundary — the pipeline
enters a **degraded-live** state rather than leaving the subscription
permanently inert with live events accumulating in the buffer: the
hydration boundary still opens so buffered and future live events keep
reconciling, but the whole catalog dependency set is dropped — the 30178
head, its 30175/30176 constituents, and any kind-5 deletion carrying a
dependency-targeting `a` tag (classified by scanning *all* `a` tags,
matching the backend's deletion router, which `find_map`s across every
tag and routes the first signer-owned coordinate — so a malformed or
foreign first `a` tag ahead of an owned 30176 cannot slip a destructive
deletion through) — because backfill never fully hydrated the owner's
constituents. Dropping only the 30178 head is not enough: the backend
refreshes the catalog head after every team/persona save, and live
delivery is newest-first, so a 30176 edit that adds a *new* member would
reach the backend before that member's 30175 and falsely tombstone a
witness-holding device's valid team. Holding the prior hydrated run's
constituents on disk only proves the *old* revision is resolvable — it
says nothing about a new member — which is why the entire dependency set
is held rather than just the head. 30177 managed-agent runtime policy
stays live (it drives no catalog refresh). A degraded device stays stale
on team/persona edits until it self-heals on the next effect re-run
(restart, or an identity/community switch) — the correct trade against
destroying valid shared state.

## Data layer

Relay paging, signature verification, NIP-33 head selection, and
untrusted-content parsing for the kind `30178` team catalog live
natively in the `fetch_team_catalog` Tauri command (`team_catalog.rs`),
structurally mirroring `fetch_persona_catalog` (`persona_catalog.rs`). A
catalog refresh crosses IPC once and never verifies a signature on the
webview thread. `teamCatalogRelay.ts` is now a thin presentation and
local-linkage layer over the verified projection — it shapes entries for
display and links each to a local team, and never parses or verifies.

Parsing is all-or-nothing, identical to the add-time re-fetch in
`add_team_from_catalog`: a team with any invalid member fails to parse
and the publication is dropped from the catalog, matching persona
behavior. **Behavior delta:** the previously reviewed partial-render of
invalid-member teams — a warning banner on an entry that could never be
added — is removed. Invalid publications are dropped entirely rather
than surfaced as un-addable.

## Hooks

`useTeamCatalogRelay.ts` mirrors the persona catalog hook: a
community-keyed query over `fetch_team_catalog`, live invalidation on
kind `30178`, share/unshare, and add-from-catalog (which invalidates
both the teams and personas stores, since adopting a team copies its
members as local personas).

## CommunityCatalogDialog

Single unified surface replacing the former separate dialogs. Agents and
Teams appear as labeled sections with type-tagged selection and a
teams-preferred launch. `TeamsSection`'s discover entry and the
new-agent card both open this one dialog. `PersonaCatalogDialog.tsx` is
removed; persona browsing now lives inside the unified dialog.

## TeamShareDialog

Publishes and unshares team catalog entries via `set_team_shared`.

## e2e + screenshots

`team-catalog.spec.ts` covers the browse + adopt flow;
`team-catalog-screenshots.spec.ts` produces the pixel-regression set.
`e2eBridge.ts` gains `mockTeamCatalogPublications`, which mirrors the
native command's head selection and shared gate and performs only a
shallow `v`/`name`/`members`-array shape check; per-member validation
stays in the Rust command.

## Follow-ups

None. The shared agent-definition text-safety policy
(Unicode-control/bidi/zero-width rejection) already covers the team
surface: #5112's `validate_team_catalog_content` chokepoint gates every
field delivered verbatim to the harness on both the publish and adopt
paths, and this PR's parse layer consumes that verified projection
rather than re-validating on the webview thread.

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
2026-08-28 14:51:25 -04:00
Taylor Ho
a2d8be5efa feat(desktop-messages): keep agents addressed across messages (#6315)
**Category:** new-feature
**User Impact:** Users can keep selected agents addressed across
consecutive messages without retyping their handles.

**Problem:** Repeated conversations with agents require manually typing
the same mentions on every turn, which adds friction and makes
recipients easy to omit.

**Solution:** The composer can now keep agents automatically addressed
per channel, either from the mention controls or after a successful
inline mention. Addressed agents remain visible in the toolbar, apply to
channel threads, survive send failures safely, and never cross community
boundaries.

## Changes

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

**desktop/src-tauri/src/events/message_tags.rs**  
Preserves the automatic-address marker on validated mention reference
tags.

**desktop/src/features/channels/ui/ChannelPane.tsx**  
Wires the appropriate channel, thread, inbox, or forum composer context
without leaking audiences across surfaces.

**desktop/src/features/communities/useCommunityInit.ts**  
Clears composer audience state when the active community changes.

**desktop/src/features/forum/ui/ForumComposer.tsx**  
Wires the appropriate channel, thread, inbox, or forum composer context
without leaking audiences across surfaces.

**desktop/src/features/forum/ui/ForumComposerAutocompletes.tsx**  
Wires the appropriate channel, thread, inbox, or forum composer context
without leaking audiences across surfaces.

**desktop/src/features/home/ui/InboxDetailPane.tsx**  
Wires the appropriate channel, thread, inbox, or forum composer context
without leaking audiences across surfaces.

**desktop/src/features/messages/lib/agentAddressMention.d.mts**  
Defines helpers and types for marked automatic-address mention tags.

**desktop/src/features/messages/lib/agentAddressMention.mjs**  
Defines helpers and types for marked automatic-address mention tags.

**desktop/src/features/messages/lib/agentAddressMention.test.mjs**  
Covers the automatic-address behavior and its failure, keyboard, layout,
or persistence boundaries.

**desktop/src/features/messages/lib/applyEditTagOverlay.mjs**  
Preserves automatic-address metadata when edited message tags are
overlaid.

**desktop/src/features/messages/lib/applyEditTagOverlay.test.mjs**  
Covers the automatic-address behavior and its failure, keyboard, layout,
or persistence boundaries.


**desktop/src/features/messages/lib/autoPinMentionedAgentsPreference.test.mjs**
Covers the automatic-address behavior and its failure, keyboard, layout,
or persistence boundaries.


**desktop/src/features/messages/lib/autoPinMentionedAgentsPreference.ts**
Stores the preference that keeps explicitly mentioned agents addressed
for later messages.

**desktop/src/features/messages/lib/extractMentionPersonas.ts**  
Separates persona recipients from the composer mention orchestration.

**desktop/src/features/messages/lib/persistentAgentAudience.test.mjs**  
Covers the automatic-address behavior and its failure, keyboard, layout,
or persistence boundaries.

**desktop/src/features/messages/lib/persistentAgentAudience.ts**  
Maintains bounded, in-memory, channel-scoped automatic agent audiences.

**desktop/src/features/messages/lib/useMentionSelection.ts**  
Centralizes mention picker selection state and agent-first selection
behavior.

**desktop/src/features/messages/lib/useMentions.ts**  
Exposes explicit picker origins and selection controls while preserving
inline mention behavior.

**desktop/src/features/messages/ui/ComposerAddressControls.test.mjs**  
Covers the automatic-address behavior and its failure, keyboard, layout,
or persistence boundaries.

**desktop/src/features/messages/ui/ComposerAddressControls.tsx**  
Renders compact addressed-agent avatars and the automatic-mention
management entry point.

**desktop/src/features/messages/ui/MentionAutocomplete.test.mjs**  
Covers the automatic-address behavior and its failure, keyboard, layout,
or persistence boundaries.

**desktop/src/features/messages/ui/MentionAutocomplete.tsx**  
Adds automatic-mention controls and options to the existing mention
picker.

**desktop/src/features/messages/ui/MessageAgentAddressPrefix.tsx**  
Shows which agents were automatically addressed on a sent message.

**desktop/src/features/messages/ui/MessageComposer.tsx**  
Integrates automatic audiences, picker controls, accessible feedback,
shortcuts, and send behavior.

**desktop/src/features/messages/ui/MessageComposer.types.ts**  
Defines the simplified channel audience context shared by composer
hosts.

**desktop/src/features/messages/ui/MessageComposerToolbar.tsx**  
Places automatic-address controls in the composer toolbar without
crowding narrow layouts.

**desktop/src/features/messages/ui/MessageRow.tsx**  
Displays automatic-address metadata alongside sent message content.

**desktop/src/features/messages/ui/MessageThreadPanel.tsx**  
Wires the appropriate channel, thread, inbox, or forum composer context
without leaking audiences across surfaces.

**desktop/src/features/messages/ui/composerAgentKeyboard.test.mjs**  
Covers the automatic-address behavior and its failure, keyboard, layout,
or persistence boundaries.


**desktop/src/features/messages/ui/persistentAgentAudienceHosts.test.mjs**
Covers the automatic-address behavior and its failure, keyboard, layout,
or persistence boundaries.

**desktop/src/features/messages/ui/useAddressMentionPulse.test.mjs**  
Covers the automatic-address behavior and its failure, keyboard, layout,
or persistence boundaries.

**desktop/src/features/messages/ui/useAddressMentionPulse.ts**  
Provides success and failure animation signals for addressed-agent
controls.

**desktop/src/features/messages/ui/useAgentAddressLockPicker.test.mjs**
Covers the automatic-address behavior and its failure, keyboard, layout,
or persistence boundaries.

**desktop/src/features/messages/ui/useAgentAddressLockPicker.ts**  
Coordinates adding, removing, and announcing automatically addressed
agents.

**desktop/src/features/messages/ui/useAlwaysAddressShortcut.ts**  
Implements the platform-aware shortcut for toggling automatic
addressing.

**desktop/src/features/messages/ui/useAutoPinMentionedAgents.ts**  
Promotes successfully sent inline agent mentions and provides a single
undoable notification.

**desktop/src/features/messages/ui/useComposerMentionPicker.ts**  
Opens the mention picker without rewriting the current draft.


**desktop/src/features/messages/ui/useMentionSendFlow.helpers.test.mjs**
Covers the automatic-address behavior and its failure, keyboard, layout,
or persistence boundaries.

**desktop/src/features/messages/ui/useMentionSendFlow.helpers.ts**  
Merges automatic and inline recipients, marks outgoing tags, and
restores failed sends safely.

**desktop/src/features/messages/ui/useMentionSendFlow.ts**  
Merges automatic and inline recipients, marks outgoing tags, and
restores failed sends safely.


**desktop/src/features/messages/ui/usePersistentAgentMentionHydration.ts**
Removes the prior draft-text hydration approach now that automatic
audiences stay at composer ingress.

**desktop/src/features/settings/ui/AgentsSettingsPanel.tsx**  
Replaces the old global behavior with explicit composer-level
automatic-mention controls.

**desktop/src/features/settings/ui/PreventSleepSettingsCard.tsx**  
Replaces the old global behavior with explicit composer-level
automatic-mention controls.

**desktop/src/shared/lib/keyboard-shortcuts.ts**  
Defines the user-facing automatic-address keyboard shortcut label.

**desktop/src/shared/ui/VideoReviewCommentMarkdown.tsx**  
Allows automatic-address prefixes to compose with video review
timecodes.

**desktop/tests/e2e/persistent-agent-audience.spec.ts**  
Covers the automatic-address behavior and its failure, keyboard, layout,
or persistence boundaries.

</details>

## Reproduction Steps

1. Open a channel with one or more agents and open the mention picker
from the composer.
2. Select an agent for automatic mentions, then send several messages
without retyping the handle; confirm the agent remains in the composer
control and receives each message.
3. Mention another agent inline, send successfully, and confirm the
agent becomes automatically addressed; use the notification's Undo
action to reverse it.
4. Open a thread in the same channel and confirm the same addressed
agents are available there.
5. Remove an agent from the composer control and confirm later messages
stop addressing it.
6. Switch communities and confirm addressed agents do not carry into the
other community.

## Screenshots

All states below use the dark Buzz theme with a selected lilac accent.

### Addressed composer

Selected agents stay visible at the composer ingress without adding
handles to the draft.

![Two automatically addressed agents in the dark composer with a lilac
accent](https://raw.githubusercontent.com/block/buzz/74c2cbbd80ed630a0c6e00c420505a691bd4994a/pr-6315--01-addressed-composer.png)

### Open mention menu

The @ ingress opens the existing mention menu and shows which agents are
already addressed.

![Open mention menu with automatically addressed agents
highlighted](https://raw.githubusercontent.com/block/buzz/74c2cbbd80ed630a0c6e00c420505a691bd4994a/pr-6315--02-open-mention-menu.png)

### Mention options

The inline options pane controls whether a successful one-time agent
mention carries into later messages.

![Automatic mention options expanded above the mention
menu](https://raw.githubusercontent.com/block/buzz/74c2cbbd80ed630a0c6e00c420505a691bd4994a/pr-6315--03-mention-options.png)

### Agent settings

The same preference is available in **Settings → Agents →
Conversations**.

![Automatic agent mentions preference in the Agents settings
pane](https://raw.githubusercontent.com/block/buzz/74c2cbbd80ed630a0c6e00c420505a691bd4994a/pr-6315--04-agent-settings.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-08-21 22:04:53 -07:00
Max Lampert
b0466ac465 perf(desktop): keep the member roster off the channel-switch path (#6456)
Switching channels triggered a full-roster fetch (kind:39002 plus a
kind:0 profile batch with every member pubkey as an author) in the
common case, and several render paths walked the full roster per render.
None of this scales past a few hundred members; the product target is
10k+.

- **Members query staleTime 30s → 5min.** Every membership change the
client can observe already invalidates the key explicitly: live
join/leave system messages for the active channel, member-added/removed
notifications for the current identity, and all membership mutations —
including previously-uncovered direct write paths (moderation kick,
agent-deletion cleanup), which now invalidate through a shared helper.
The 30s window bought correctness we already had and charged a roster
fetch per switch.
- **ChannelMembersBar no longer mounts the roster query for non-DM
channels** — the count renders from the channel summary, and the
private-channel huddle gate accepts `channel.isMember` (derived from the
same kind:39002 event as the roster's self entry).
- **Roster-derived lookups are cached on roster identity**
(`rosterDerivations.ts`): role map, agent-member subset, member/bot
pubkey sets. These were rebuilt O(members) on every live message /
profile re-key. React Query's structural sharing keeps the roster
identity stable, so each derivation computes once per distinct roster.
- **Backend: the kind:0 profile join in `get_channel_members` is capped
at the first 500 members** (roster order). Members past the cap keep
`display_name: None` (UI falls back to pubkey labels and profile
caches); `role=="bot"` agent flags are roster-derived and unaffected.
Full roster pagination is the structural follow-up.
- **Composer keystroke path**: `useCanAddChannelMembers` re-scanned
channels + roster per keystroke; now memoized on data identities,
sharing the cached pubkey set.

### Measured / estimated impact

| metric | before | after |
|---|---|---|
| roster fetches while switching (live trace) | nearly every switch | ≤1
per channel per 5 min |
| roster fetch cost on the wire (live, 51-member channel) | 273ms per
fetch | amortized away |
| kind:0 `authors` filter size at 10k members | ~670KB per request (~67
B/pubkey) | capped at 500 authors (~34KB) |
| warm-switch longtask at 10k members (mock harness, 4× throttle) |
364ms | 318ms |
| per-render roster walks (role map, agent sets) at 10k members |
O(members) per live message | once per distinct roster |

Deferred deliberately: protocol-level roster pagination and removing
`memberPubkeys` from channel summaries (needs relay support).

---------

Signed-off-by: Max Lampert <maxwell@squareup.com>
2026-08-21 17:40:20 -06:00
Will Pfleger
c63ac746cd perf(desktop): split discover_acp_providers into cheap and forced paths (#6330)
Splits the `discover_acp_providers` Tauri command into a cheap cached
path and an explicit forced re-discovery path.

The profiling flight-recorder identified `discover_acp_providers` as the
acute UI-freeze correlate: a 20–65s probe pipeline (PATH re-fetch, CLI
auth probes, process spawns) was running on routine renders like channel
switches, because hot surfaces remounted the wrapping query and
automatic staleness refetches re-ran the full pipeline. This is
complementary to the idle auto-reload (#5588) and bounded-accumulator
(#5596) work.

## Backend
- Added a `force` parameter to `discover_acp_providers` /
`discover_acp_runtimes_from`. The default (cheap) path serves cached
availability and auth statuses with zero process spawns; `force: true`
runs the full pipeline (cache clear, PATH re-fetch, CLI auth probes).
- Added a single-flight coalescer (`forced_single_flight`) so concurrent
forced runs share one probe instead of stacking.
- Extracted the auth-status cache and `resolve_auth_statuses` into
`discovery/auth_status_cache.rs`.

## Renderer
- Raised `staleTime` to 30 min; forced refresh flows only through an
explicit `fetchQuery` path (`refreshAcpRuntimes`), so automatic
staleness refetches can never re-run probes.
- Added `useAcpRuntimesQueryForced` for Settings and onboarding
surfaces; connect/install/save/delete mutations force-refresh in
`onSettled`.
- Switched `SetupStep`, `HarnessCatalogDialog`, and
`HarnessesSettingsPanel` to the forced hook.

## Structure
The touched files were at or over the grandfathered 1000-line file-size
ceiling, so the change is delivered as cohesive extractions rather than
in-place growth: `forced_single_flight.rs`, `auth_status_cache.rs`,
`discovery/tests/forced_discovery.rs`, `acpRuntimesQuery.ts`, and
`tauriAcpDiscovery.ts`. Dependencies are one-directional (`hooks.ts`
imports from `acpRuntimesQuery.ts` and re-exports the forced hook, so
consumer imports are unchanged; no import cycle).

## Tests
Added Rust tests pinning the cheap-vs-forced split: a probe is spawned
only when forced, the cheap path reuses the cache, and auth status is
`Unknown` before any probe runs.

---------

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
2026-08-20 19:44:05 -04:00
morgmart
9a1e861ab8 Add appearance preference previews (#6193)
## Summary

- Replace the Link previews and Thread layout dropdowns with the shared
segmented control from #5644.
- Pair each setting with a real, inert preview: production Compact/Rich
link cards and a simplified Focus/Split thread diagram.
- Keep scrub previews local to Settings so only a completed selection
updates the saved preference and real app behavior.
- Keep each setting and preview responsive as one grouped unit, with
focused light/dark and narrow/wide coverage.

## Dependency

- Stacked on #5644, which provides the typography/density foundation,
shared `SegmentedControl`, and responsive `SettingsOptionRow`.
- After #5644 merges, this PR should be retargeted/rebased onto `main`.

## Validation

- `pnpm typecheck`
- `pnpm check:px-text`
- `pnpm check:file-sizes`
- `pnpm exec playwright test tests/e2e/appearance-previews.spec.ts
--project=smoke` (3 passed)
- Focused existing Appearance coverage (2 passed)
- Pre-push desktop checks, typecheck, tests, and Tauri checks

Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com>
2026-08-19 14:32:17 -07:00
klopez4212
7e2651791d Add font size and conversation density preferences (#5644)
## Summary

- Add separate **Font size** and **Conversation density** controls in
Appearance.
- Use a 13 / 14 / 15px conversation text ramp for Smaller / Default /
Larger while scaling interface typography through a shared virtual-rem
system.
- Keep layout geometry fixed while typography changes, and compose Cmd
+/- text zoom on top of the selected preference.
- Let Compact, Comfy, and Spacious control conversation row spacing plus
paragraph and list rhythm in Markdown content across Buzz.
- Preview font size and density together with click-and-drag comparison;
interrupted scrubbing restores the saved preference.
- Synchronize preferences across open Buzz windows, including full
local-storage clears during sign-out or dev reset.
- Promote the segmented control to shared UI and make Settings rows
adapt to their card width.

## Validation

- `just ci`
- 4,965+ desktop unit tests across the final merged branch
- 1,465 mobile tests through the full repository gate
- Focused Playwright coverage for font-size/density independence, drag
preview cancellation, Inbox geometry, thread rendering, keyboard zoom,
and cross-surface typography
- Fresh E2E build against the final merged branch

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com>
Signed-off-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz>
Co-authored-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz>
Co-authored-by: morgmart <98432065+morgmart@users.noreply.github.com>
2026-08-18 15:24:09 -07:00
klopez4212
43e53fc349 Standardize settings section layout (#5855)
## Summary

- move Settings section labels outside their framed containers and
centralize the spacing
- apply the shared hierarchy across Appearance, Notifications, Voice,
Agents, Shortcuts, Members, and Profile
- give Identity and Sign out complete section treatments while removing
redundant in-cell labels

## Testing

- desktop pre-push checks, including 4,791 tests
- focused Settings layout and sign-out Playwright coverage

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
Signed-off-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz>
Co-authored-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz>
2026-08-14 16:25:40 +01:00
klopez4212
0571f5455b Polish glass Huddle tray behavior (#5590)
## Summary

- inset the in-app Huddle tray with four rounded corners and even 8px
spacing when Glass background is enabled
- keep the popped-out Huddle dock full-width
- hide and suppress Glass background on Linux

## Why

The in-app tray reused the opaque backing needed by non-glass windows,
which covered the native vibrancy around it. Linux does not support this
window treatment.

## Testing

- `pnpm -C desktop build:e2e`
- focused Appearance and Huddle Playwright smoke tests
- pre-push desktop checks, typecheck, and 4,666 unit tests

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
Signed-off-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz>
Co-authored-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz>
2026-08-13 15:40:50 -07:00
klopez4212
cd2aa5c12d Add glass appearance and cohesive settings (#5478)
## Summary

- add an opt-in native glass sidebar with opacity controls and live
theme previews
- refine sidebar spacing and Buzz-only active rows while preserving
production defaults
- unify settings section cards, subtitles, and agent runtime rows

## Validation

- repository format, lint, type, and file-size checks
- 4,538 desktop tests and 2,270 native desktop tests
- desktop and web production builds
- 1,261 mobile tests in the completed full gate
- focused Playwright appearance, sidebar, settings, pairing, and runtime
coverage

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
Signed-off-by: Kenny Lopez <klopez4212@gmail.com>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Signed-off-by: Fast Fizz <2df81cb51f05a9d5387ef24d7b9ecb8fcdfcd1c70ffabc67061c9596e1b5b1c4@buzz.block.builderlab.xyz>
Co-authored-by: Carl <3c4caeafb646d23867f1c4832e68211d77e2561946171625f75c3ce1a3f2670f@buzz.block.builderlab.xyz>
Co-authored-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Co-authored-by: Fast Fizz <2df81cb51f05a9d5387ef24d7b9ecb8fcdfcd1c70ffabc67061c9596e1b5b1c4@buzz.block.builderlab.xyz>
2026-08-11 10:25:23 -07:00
Taylor Ho
1922d49cb2 feat(desktop): adding rich link previews to messages (#3818)
## 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 |
|---|---|
| ![Compact composer
loading](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/3818/compact-composer-loading.png)
| ![Compact composer
ready](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/3818/compact-composer-ready.png)
|

### Rich composer

| Loading | Ready |
|---|---|
| ![Rich composer
loading](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/3818/rich-composer-loading.png)
| ![Rich composer
ready](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/3818/rich-composer-ready.png)
|

### Responsive composer

| 800px loading | 800px ready |
|---|---|
| ![800px composer
loading](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/3818/composer-800-loading.png)
| ![800px composer
ready](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/3818/composer-800-ready.png)
|

| 420px loading | 420px ready |
|---|---|
| ![420px composer
loading](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/3818/composer-420-loading.png)
| ![420px composer
ready](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/3818/composer-420-ready.png)
|

### Recipient presentation

| Compact | Rich |
|---|---|
| ![Recipient
compact](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/3818/recipient-compact.png)
| ![Recipient
rich](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/3818/recipient-rich.png)
|

### Display-text Markdown link

| Composer | Recipient |
|---|---|
| ![Display-text link in
composer](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/3818/display-text-composer.png)
| ![Display-text link with recipient
preview](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/3818/display-text-recipient.png)
|

### Rich multiline description

![Rich preview preserving description
paragraphs](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/3818/rich-multiline-recipient.png)

### Immediate dismissal

| Before × | Immediately after × |
|---|---|
| ![Preview before immediate
dismissal](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/3818/dismissal-before.png)
| ![Composer immediately after preview
dismissal](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/3818/dismissal-after.png)
|

---------

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
2026-08-07 10:56:08 -07:00
klopez4212
480c41ebf1 Improve desktop mobile pairing flow (#5024)
## Summary
- add stable three-step guidance to desktop mobile pairing
- move code confirmation inline and show animated completion states
- preserve pairing reset behavior and reduced-motion support

## Test plan
- `pnpm --dir desktop check`
- `pnpm --dir desktop exec tsc --noEmit`
- `pnpm --dir desktop exec playwright test
tests/e2e/mobile-pairing-qr.spec.ts --project=smoke`
- pre-push desktop suite: 4,387 tests passed

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
Co-authored-by: Fizz <50a12680c76f1a52c0b7af8dbb17e02c583227c290fb93b9a3defb456114223f@buzz.block.builderlab.xyz>
2026-08-06 07:45:21 -07:00
Taylor Ho
43cced308d feat(desktop): sync themes per community (#3653)
**Category:** new-feature
**User Impact:** Users can keep a distinct Appearance scheme for each
community and restore it on another desktop signed in with the same
identity.

**Problem:** A single global theme makes it harder to distinguish among
communities, and local-only preferences do not follow a user to another
device. **Solution:** Save each community's stable theme, accent, and
system-following selection as private encrypted relay state, backed by a
responsive local cache and guarded against switch races, invalid future
records, and relay failures.

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

**desktop/src/app/App.tsx**
Mounts the community-scoped theme controller inside the active community
lifecycle.

**desktop/src/features/settings/lib/appearanceScopeCopy.test.mjs**
Covers active-community and fallback labels used to explain Appearance
scope.

**desktop/src/features/settings/lib/appearanceScopeCopy.ts**
Builds a safe, trimmed label for the currently active community.

**desktop/src/features/settings/ui/SettingsPanels.tsx**
Clarifies which Appearance controls are per-community and which apply
globally when multiple communities exist.

**desktop/src/shared/constants/kinds.ts**
Defines the NIP-78 application-data event kind used for theme
preferences.

**desktop/src/shared/theme/CommunityThemeController.tsx**
Coordinates cached appearance, encrypted relay retrieval, live updates,
reconnect behavior, and safe community switching.

**desktop/src/shared/theme/ThemeProvider.tsx**
Exposes a single appearance application path so synchronized preferences
use the existing renderer and persistence behavior.

**desktop/src/shared/theme/communityThemePreference.test.mjs**
Covers contract validation, user/relay isolation, malformed records,
cache failures, and switch-race decisions.

**desktop/src/shared/theme/communityThemePreference.ts**
Defines the versioned stable preference contract, safe defaults, local
cache keys, and persistence guards.

**desktop/src/shared/theme/communityThemeSync.test.mjs**
Covers relay absence, unreadable records, unavailability, seeding
safety, and teardown of pending writes.

**desktop/src/shared/theme/communityThemeSync.ts**
Encrypts theme preferences to the user, publishes and retrieves NIP-78
state, and handles ordering and lifecycle safety.

</details>

### Reproduction steps

1. Join at least two communities and open **Settings → Appearance**.
2. Choose a different theme, accent, or system-following mode in each
community.
3. Switch between the communities and verify each one restores its own
scheme without overwriting the other.
4. Sign in on another desktop with the same Nostr identity, join the
same community, and verify its saved scheme is restored from that
community's relay.
5. Disconnect the relay, change Appearance, and verify the UI remains
responsive and the local fallback is retained.

### Screenshots / demos
<img width="1733" height="948" alt="image"
src="https://github.com/user-attachments/assets/5afeabaa-0def-482c-9b87-8a880ee0a467"
/>


<img width="700" height="412" alt="Screen Recording 2026-07-29 at 4 39
52 PM"
src="https://github.com/user-attachments/assets/d58da329-aec5-4324-a4b2-cbcb2702a81a"
/>

---------

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz>
Co-authored-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz>
2026-08-05 13:18:48 -07:00
Wes
c1b88af8d7 feat(desktop): improve channel template discovery (#4549)
## Summary

- move **Channel templates** from Communities to Personal settings
- always expose the template picker in New Channel, using **None** as
the no-template value
- create a channel template directly from the picker and select it on
return
- preview the selected template's current visibility, canvas, agents,
and teams
- order the channel-creation controls as **Type / Visibility /
Template** and mark Template **Optional**
- cover populated and empty libraries, inline creation, selection,
visibility overrides, mixed agent/team inventory, field order, optional
labeling, and settings navigation in Playwright

## Validation

Validated at desktop-only tip `76442270c88aa1d533ddca5de9f87cd615183919`
with a clean worktree:

- focused channel-template Playwright: 2/2 passed
- Type / Visibility / Template ordering and muted Optional treatment
visually inspected in the replacement screenshot
- `git diff --check origin/main...HEAD` passed
- PR diff contains exactly nine Desktop files and no Mobile files

The pre-push hook was bypassed only for the corrected history push
because the inherited Mobile test `keeps follow mode off while a tall
newest message stays visible` passes in Linux CI but fails on macOS
because its offscreen-child mounting assertion is platform-sensitive. No
Mobile code or tests are changed by this PR.

## Screenshot

![New Channel with Type, Visibility, and optional
Template](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/4549/create-channel-type-visibility-template.png)

Originating Buzz channel: `efba7343-e147-48b7-a2aa-15a5f04abc57`

---------

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
2026-08-03 09:30:15 -07:00
John Matthew Tennant
c104eecfb3 feat(desktop): import local Pocket voices (#3259)
## Context

Pocket TTS currently offers bundled reference voices. People also need a
local, private way to add a voice without sending audio to a cloud
service.

## Summary

Add a Pocket voice import flow to Voice settings. Buzz opens the native
file picker, decodes common audio formats in the reusable `buzz-voice`
crate, canonicalizes the selected audio, stores it under a
content-derived identity in app data, selects it, and lets the user
delete it later.

## Changes

- Accept WAV, M4A, MP3, FLAC, OGG, and AIFF files between 2 and 30
seconds, including multichannel sources.
- Decode and downmix accepted audio to canonical mono 32 kHz PCM16 WAV
before hashing and storage.
- Store imported voices behind stable `pocket:imported:<sha256>`
identities and content-addressed files.
- Keep absolute file paths inside the native process and expose only
voice metadata to React.
- Include imported voices in Pocket preview and live huddle playback.
- Add Add voice and delete controls while preserving the bundled Pocket
voice catalog.
- Fall back to Mary when the selected imported voice is deleted.
- Keep durable import, selection, and deletion successful when a live
TTS worker acknowledgement is delayed.
- Preserve bundled voices when optional import metadata is unreadable
and keep failed deletion retryable.

## Related issue

None found.

## Testing

Production decoding was exercised with WAV, M4A with AAC, MP3, FLAC, OGG
Vorbis, and AIFF fixtures. Each format canonicalized to mono 32 kHz
PCM16 WAV. Manual validation in the combined daily-driver build covered
native-picker import, Preview, live-huddle playback, deletion, and Mary
fallback.

## Screenshots

The Voice settings card preserves the bundled Pocket catalog and adds
the local Add voice action.

![Pocket TTS voice
import](https://raw.githubusercontent.com/block/buzz/c03ba29060ca544c5ac3394c212f376651b386a3/pr-3259--pocket-voices.png)

## Reviewer-reproducible examples

Create common-format fixtures and run them through the production
importer:

```bash
. ./bin/activate-hermit
fixtures="$(mktemp -d)"
ffmpeg -hide_banner -loglevel error -f lavfi -i "sine=frequency=220:duration=3" -ac 2 -ar 44100 "$fixtures/voice.wav"
ffmpeg -hide_banner -loglevel error -i "$fixtures/voice.wav" -c:a aac "$fixtures/voice.m4a"
ffmpeg -hide_banner -loglevel error -i "$fixtures/voice.wav" "$fixtures/voice.mp3"
ffmpeg -hide_banner -loglevel error -i "$fixtures/voice.wav" "$fixtures/voice.flac"
ffmpeg -hide_banner -loglevel error -i "$fixtures/voice.wav" -c:a libvorbis "$fixtures/voice.ogg"
ffmpeg -hide_banner -loglevel error -i "$fixtures/voice.wav" -c:a pcm_s16be "$fixtures/voice.aiff"
BUZZ_VOICE_IMPORT_TEST_DIR="$fixtures" \
  cargo test -p buzz-voice imports_common_audio_format_fixtures -- --ignored --nocapture
```

Exercise import persistence, synthesis, deletion, and bundled-voice
fallback with an installed Pocket model:

```bash
BUZZ_POCKET_MODEL_DIR=/path/to/pocket-model-bundle \
  cargo test -p buzz-voice --test pocket_import_audio \
  objective_import_synthesis_delete_and_mary_fallback \
  -- --ignored --nocapture
```

Exercise the native-picker boundary, selection, preview dispatch,
deletion, cancellation, and invalid-file states:

```bash
cd desktop
pnpm build:e2e
pnpm exec playwright test tests/e2e/voice-settings.spec.ts --project=smoke
```

---------

Signed-off-by: John Tennant <jtennant@block.xyz>
Signed-off-by: John Tennant <johnmatthewtennant@gmail.com>
Signed-off-by: John Tennant <jtennant@squareup.com>
Signed-off-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz>
Co-authored-by: John Tennant <jtennant@block.xyz>
Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz>
2026-07-31 11:15:09 -04:00
Tyler
61ba9dfaa0 refactor(voice): extract reusable Pocket primitives + Pocket voice settings (relands #2467 + #3208) (#3910)
Relands **#2467** (extract `buzz-voice` crate) and **#3208** (Pocket
voice settings) onto main, after #3266 and #3180 merged.

## Why a fresh PR
The repo is squash-only with delete-branch-on-merge. Squashing #3266
deleted `jtennant/pocket-tts-2026-04`, which was #2467's base — GitHub
auto-closed #2467 and it cannot be reopened. Squash merges also sever
ancestry, so GitHub's natural merge-base reports phantom conflicts for
the whole remaining stack.

## Content provenance
- Byte-identical to the blessed `jt/buzz-voice-refactor` branch
(`93029c577`, tree `6729e0eff` — reviewed by Dawn (#2467) and Max
(#3208) at exact heads) **except** the three files where #3180 and #3208
genuinely interact.
- Three-file resolution (union of both sides):
- `huddle/mod.rs` — #3180's pipeline re-exports + #3208's
`agent_tts_routing` imports.
- `huddle/state.rs` — `reset_preserving_generation` preserves both
`huddle_generation` (#3180) and `tts_enabled` (#3208); test sets merged
into one `tests` module.
- `desktop/src/testing/e2eBridge.ts` — both switch arms kept; no
duplicate case labels.

## Verification at cf32dacd7
- `cargo test` (desktop/src-tauri, pinned 1.95.0): **2047 + 3 pass / 0
fail** (14 ignored: 8 keychain, 4 real_relay, 2 flag-gated)
- `cargo clippy --all-targets -- -D warnings`: clean; `cargo fmt
--check`: clean
- `cargo check --workspace` (root, includes new `buzz-voice` member):
clean; `cargo test -p buzz-voice`: 5/0
- `pnpm test`: **3885 / 0**; `tsc --noEmit`: clean; lint: clean

The 3180×3208 interaction resolution is getting an independent team
re-review before merge.

Buzz channel: buzz-desktop-voice `fd5fb402-b651-4238-89b1-bb3e2fa4dc96`,
thread `b4798ecc`.

Signed-off-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
2026-07-31 09:41:09 -04:00
Tyler
468647a51f feat(desktop): locally stored NIP-49 encrypted key backup (#2937)
## Summary

Adds a locally stored **NIP-49 encrypted key backup** (`ncryptsec`) to
the desktop app, per the plan reviewed in buzz-development (Rev 3,
approved 9/10 by Wren; implementation also reviewed and approved 9/10).

**Two-artifact design — canonical bytes originate entirely in Rust:**
- `create_ncryptsec_backup` runs under the `identity_mutation` lock:
encrypt → decrypt-verify against the live pubkey → atomic `0o600` write
to `{app_data_dir}/identity.ncryptsec` → reread/byte-compare → return
the exact persisted bytes. The frontend never re-derives or re-encrypts.
- `save_ncryptsec_copy` writes a portable copy via the save dialog
(parse-gated, secret-file semantics) and never mutates canonical state.
- `generate_backup_passphrase`: 6 words from the EFF short wordlist via
`OsRng` (custom passphrases min 12 chars).
- Import accepts `ncryptsec1` with optional password; the raw-`nsec`
path is untouched. Different-pubkey import and sign-out wipe the
app-managed backup (post-commit, best-effort — a failed import can never
destroy the still-live identity's backup; regression-tested).

**Never-relay guarantee (egress guard + tripwires):**
- `egress_guard.rs` fail-closed at all 8 `/events` submission boundaries
(relay submit funnel, 3× `relay.rs`, huddle STT, both engram submitters,
native WS choke point), rejecting `ncryptsec1`/`NCRYPTSEC1` in text and
binary frames. Scope is deliberately ncryptsec-only: pairing
intentionally carries raw nsec inside its encrypted session.
- Site-granular `/events` inventory tripwire: per-file (`/events` count,
guard-call count) pairs; unlisted files expect zero. Mutation-style
tests prove a ninth site in an existing file, a removed guard, and a new
unlisted file all fail the scan.
- ncryptsec source-allowlist scans in **both** trees (Rust + TS).

**Frontend:** onboarding `BackupStep` is encrypted-by-default — the
default path never invokes `get_nsec` (e2e asserts the command log).
Raw-nsec export stays behind an explicit click with prior semantics.
Shared `EncryptedBackupCreator` powers onboarding + a new settings row;
the import form auto-switches to encrypted mode on `ncryptsec1` paste
(case-insensitive HRP).

**Open product call for @tlongwell-block:** onboarding default is
*encrypted* in this PR; flipping to raw-default is a small change either
way (documented in the plan).

Review history: plan Rev 3 and the implementation were both iterated
with Wren to 9/10 (two blockers from round 1 — import ordering,
inventory granularity — plus an uppercase-bech32 hardening gap, all
fixed in `dde37183e`). Thread: buzz-development.

### Related issue

Follow-up to the direction explored in #385 (NIP-PB, closed) — this
ships local NIP-49 (the standard) instead of a new NIP. No open
duplicate found.

### Testing

All at exactly `dde37183e` (same shell, HEAD verified):

- `cargo test` — 1680 passed / 0 failed / 14 ignored (includes a
deliberate ~70s log_n-18 NIP-49 round trip, spec vector, wrong-password,
NFKC, uppercase-vector decrypt, injection test per egress boundary,
inventory mutation tests, import-ordering regression tests)
- `cargo clippy --all-targets -- -D warnings` — clean; `cargo fmt
--check` — clean
- `pnpm typecheck` — clean; JS unit suite 3529/3529; biome (repo-pinned
2.4.16) clean
- Playwright `onboarding-backup` / `onboarding` /
`onboarding-agent-defaults` / `profile-nsec-reveal` — 86 passed, 1 known
avatar-reservation flake (passed on rerun; untouched by this diff).
`passThroughBackupStep` now exercises the encrypted default, so every
downstream onboarding spec covers the new path.
- Note: browser e2e fakes the crypto via the mock bridge (fixed
spec-vector blob); decryption correctness is proven in the Rust tests.

## Latest onboarding integration

The current head adds an additive `IdentityInfo.storage` field
(`ephemeral`, `system-keyring`, `local-file`, or `environment`) so
onboarding can accurately explain where the active identity is
protected. It surfaces storage metadata only—never key material—and
leaves the existing lost/keyring-locked recovery behavior intact.

---------

Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz>
2026-07-30 20:58:03 -04:00
Alex Kemper
f3e5e81267 fix(catalog): update Amp tagline (#3806)
## Summary

Update Amp's runtime catalog description to use its current tagline:

> The coding agent and development environment that runs anywhere and
everywhere.

### Related issue

N/A. This follows the Amp description update in
https://github.com/block/buzz/pull/3758.

### Testing

* `pnpm -C desktop check`
* `pnpm -C desktop typecheck`
* `pnpm -C desktop test` (3,835 passed)

No screenshot is included because this changes only the catalog
description text. It does not change layout or interaction behavior.

Signed-off-by: AJKemps <AJKemps@users.noreply.github.com>
Co-authored-by: AJKemps <AJKemps@users.noreply.github.com>
Co-authored-by: Alex Kemper <alex@ampcode.com>
2026-07-30 22:54:41 +00:00
Bradley Axen
ede2686334 fix(desktop): align data deletion labels (#2230)
## Why

The Profile settings action still says “Sign Out,” while its
confirmation action says “Delete My Data.” Both buttons trigger the same
destructive local-data wipe and should name it consistently.

## What

- Label both destructive actions “Delete my data”
- Assert the matching section and confirmation labels in the existing
Playwright coverage

## Risk Assessment

Low — copy and test assertions only; sign-out behavior is unchanged.

## References

- Follow-up to #2208
- #2216 also touches this copy and should preserve “Delete my data” when
rebased
- `just desktop-check`
- `just desktop-test` (3,275 tests)
- Desktop E2E build and sign-out Playwright spec (2 tests)

Generated with Codex

Signed-off-by: Bradley Axen <baxen@squareup.com>
2026-07-30 15:47:35 -07:00
Will Pfleger
d40a33290e feat(desktop): raise the install ceiling and make installs observable (#3368)
Windows installs of Goose and other harnesses failed at exactly five
minutes with an empty error (#2401). The 300s ceiling was killing
installs that were working, just slowly — the Goose step pulls a ~79MB
release asset, and Windows Defender scans every file npm extracts. When
the ceiling fired it discarded the output it had already read, so the
user got a bare timeout string and no way to tell a hang from a large
download.

## The ceiling

`INSTALL_TIMEOUT` is 900s, and the error names the limit: `install
command exceeded the 15m ceiling and was terminated`. It stays a pure
wall-clock ceiling with no inactivity kill — nothing observable
distinguishes a hung installer from one silently transferring a large
artifact, so silence alone never kills an install. A ceiling kill
remains non-retryable; re-running a command that already burned 15
minutes costs the user more time with no plausible path to success.

The child's exit and both stream drains fold into one resumable settle
governed by a single deadline. Waiting on the drains outside that
deadline would let a descendant that outlived the install shell hold the
output pipes — and the per-runtime install guard behind them — open with
no bound, which is the failure the ceiling exists to prevent. So the
deadline path terminates the process group on the normal-exit branch
too: a leader that exited with a real status still gets its stragglers
killed, and the guard cannot stick either way. Whether the leader had
already exited only decides the verdict — its real status outranks a
timeout.

The install shell is a session leader and its descendants inherit the
output pipes, so signalling only the leader left them running and the
drains blocked on a pipe nobody would close. Escalation keys off the
*group's* liveness rather than the leader's, since a descendant that
ignores SIGTERM outlives the leader and would otherwise never receive
the group SIGKILL. Reaping the killed child and finishing the drains
share one bounded grace, so a termination that failed outright cannot
extend the ceiling that just fired.

## Output capture

Each stream drains into a bounded capture that is *shared* with the
reader rather than returned by it, so whatever arrived before a stall is
readable at the ceiling — exactly when the output matters most. Output
of any size costs a fixed amount of memory.

One capture holds two independently bounded views of the same bytes:

| View | Head / tail | Cut marker |
|------|-------------|------------|
| UI (`InstallStepResult`) | 512 B / 1024 B | `... (N bytes omitted)
...` |
| Log file | 128 KiB / 128 KiB | `... [N bytes omitted at cap] ...` |

The UI budget is screen space; the log's is disk. Both markers are
inline, so neither ever implies completeness it does not have. Both ends
are cut at arbitrary byte offsets, so a partial character is trimmed and
the partial token each cut left behind is dropped — the marker's byte
count includes both trims.

## Install log

`steps` carries only the last attempt of each step, truncated for
display. Everything else — earlier retries, the prerequisite step that
actually broke, the managed-Node bootstrap — used to be discarded.
`InstallReporter` now appends one self-contained record per attempt of
per step to `install-<runtime-id>.log` beside the agent logs, and
`InstallRuntimeResult.log_path` carries the file to the UI, where a
failure message ends with `Full log: <path>`.

Each record is bounded independently by the log-scale capture that
produced it, so a first attempt that printed megabytes cannot push out
the later record explaining the failure; the run's total is bounded by
steps × attempts × per-record cap. Every early return builds its result
through one `InstallReporter::failed` helper, so no failure path can
omit the log pointer, and synthesized steps go through `record_step` — a
step that reaches the UI without passing it would be invisible in the
file.

Install output can echo a registry token or proxy credential from the
environment it ran in, and the file is written unattended. Redaction
keys off the *names* of the environment variables the install inherited,
snapshotted once per run, rather than a list of known secret value
prefixes: a credential with no recognisable shape is exactly the one a
prefix match misses. Three name rules apply, because the variables need
different treatment:

| Rule | Variables | Redacted |
|------|-----------|----------|
| URL userinfo | `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`,
`NPM_CONFIG_PROXY`, `NPM_CONFIG_HTTPS_PROXY`, `NPM_CONFIG_REGISTRY` |
`user:password` only |
| Exact name | `NPM_CONFIG_KEY`, `NPM_CONFIG__AUTH`, `NPM_CONFIG_OTP` |
whole value |
| Marker substring | `*TOKEN*`, `*SECRET*`, `*PASSWORD*`, `*_PAT`, … |
whole value, 8-byte floor |

A proxy or registry keeps its host and port, because an install that
fails behind one is diagnosable only if the record still says which one
it went through, and a bare `user@` with no password is not treated as a
credential. npm's own settings are listed by exact name rather than
matched on `KEY` or `AUTH` substrings — both occur throughout an
ordinary environment on values that are paths and people's names — and
they bypass the 8-byte floor, since a six-digit one-time password is a
credential at that length. Matching is case-insensitive, which is what
npm's lowercase `npm_config_*` spelling needs. `0o600` is set by the
create rather than a later `chmod`, which would leave a window where the
umask decides. A runtime id that cannot safely be a filename yields no
log rather than a sanitized one — a rewritten id could collide with
another runtime's log.

The file holds exactly one run. A run opens its own session after the
runtime id has been canonically resolved — the previous file rotates to
`.1` and any older `.1` is removed before the rename, since a rename
that will not replace its destination would otherwise wedge rotation
permanently on Windows. The session writes a header naming the runtime,
the app version (`app.package_info().version` on the Rust side — cannot
be mocked or fail), the OS (`std::env::consts::OS`), and the start time:
a Windows failure and a macOS one on the same runtime are different
bugs, and a stale app version explains a failure that no longer
reproduces. Each record carries its attempt's elapsed time.

## Live output line

A 15-minute ceiling with nothing behind it but a spinner is
indistinguishable from a hang. The same drain seam feeds an
`acp-install-output` event carrying the newest complete line, and the
three install entry points — Doctor harness rows, the harness catalog
dialog, and onboarding runtime cards — render it under the spinner with
`aria-live="polite"`.

Ordering is keyed on a `seq` monotonic across the whole install, not on
the attempt number, which restarts at 1 for every step: keyed on
attempt, one step succeeding on attempt 2 would make the next step's
attempt-1 output look stale and freeze the display for the rest of the
install. Each executed attempt begins with an unthrottled `line: null`
clear signal, so a stale failure line cannot sit under the spinner while
the retry runs. Events are otherwise throttled to four per second, and
the throttle *retains* the newest pending line and flushes it when the
window reopens rather than dropping it — at an attempt boundary a drop
would silently eat the new attempt's first line.

The subscription is mounted for the runtime's whole lifetime rather than
started when the install begins. The install command is invoked from the
click handler, so the clear and a fast command's first lines can be
emitted before React has committed the pending state, and nothing
replays them — a subscription that waited for that state would lose the
entire output of a short install. The run boundary resets the ordering
key when the install settles, since `seq` restarts for the next run, and
the line renders only while installing, so a straggler from a finishing
drain cannot appear under a fresh Install button.

The 15-minute ceiling deliberately stops waiting on stuck drain threads
— a hung installer must not freeze the app. That means a drain thread
can outlive its `InstallReporter`. Without a generation guard, a drain
that calls `offer` after the run settles would publish an event with the
run's high `seq`, poison the permanent listener's React state, and cause
the next install's restarted `seq=0` events to be rejected. `Live` now
carries a `lifecycle: Arc<RwLock<bool>>`; drain threads hold a **shared
read guard** from the admission check through the `(self.emit)(...)`
call, making the check-then-emit pair atomic with respect to shutdown.
`InstallReporter::drop` takes the **exclusive write guard** and stores
`false` — this blocks until every in-flight drain publication releases
its read guard, then prevents any new admission. Deactivation is
bounded: the write lock holds only for the flag store, so it can block
at most for the duration of one emit call (microseconds to low
milliseconds). Rust drops locals in reverse-declaration order, so
`reporter` drops before `_guard`, ensuring the exclusive write completes
before the per-runtime concurrency guard releases and a new install can
start.

## Also

Install result types move to `desktop/src/shared/api/installTypes.ts`,
following the existing `searchTypes.ts` / `workflowTypes.ts` convention,
and are re-exported from `tauri.ts` and `types.ts` — both already over
the file-size cap, so neither can grow to carry them.

Two comments described `AdapterOutdated` as applying only to the
deprecated package; it also covers a version below the supported floor.

Report: #2401

---------

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
2026-07-30 17:07:22 -04:00
Taylor Ho
bd0bff24bf feat(desktop): add password-protected backups in settings (#3701)
**Category:** new-feature
**User Impact:** Users can create, download, and verify a
password-protected backup of their private identity from desktop
Settings.

**Problem:** Buzz does not currently give signed-in users a
Settings-based path to protect or validate their private identity
independently of onboarding. **Solution:** Add a focused backup menu to
the private-key row, keep encryption and verification local in Rust, and
preserve completed encrypted backups briefly so native saves can be
retried without repeating encryption.

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

**desktop/src/features/settings/**
Adds the background backup lifecycle, create and test dialogs,
private-key menu integration, password handling, and focused unit
coverage.

**desktop/src/features/onboarding/ui/NsecMaskedDisplay.tsx**
Extends the masked private-key display with reusable overflow-menu
actions used by Settings.

**desktop/src/app/App.tsx**
Mounts the backup provider at app scope so encryption and save work
survive closing Settings or the modal.

**desktop/src/shared/api/tauriIdentity.ts**
Adds typed desktop bindings for local backup creation, save, selection,
and verification.

**desktop/src-tauri/src/key_backup.rs and
desktop/src-tauri/src/commands/identity.rs**
Implements local NIP-49 encryption, password generation, file handling,
and public-identity-only verification results.

**desktop/src-tauri/src/egress_guard.rs and guarded call sites**
Blocks encrypted secret material from relay, websocket, snapshot,
sharing, and huddle egress paths.

**desktop/src-tauri tests and fixtures**
Covers encryption, verification, file behavior, and fail-closed
no-egress protections.

**desktop/src/testing/e2eBridge.ts, desktop/tests/, and
desktop/playwright.config.ts**
Expands the mock native bridge and browser coverage across create,
retry, expiry, and current/different-identity verification states.

**desktop/src-tauri/Cargo.toml, Cargo.lock, and assets**
Adds the local cryptography/password-generation dependencies and
embedded short-word list.

</details>

## Reproduction steps

1. Run the desktop app and open **Settings → Profile → Identity**.
2. Open the private-key overflow menu and choose **Create backup**.
3. Enter or generate a valid password, submit, and confirm progress
continues if the dialog or Settings is closed.
4. Save the resulting `.ncryptsec` file; cancel and retry to confirm the
temporary download remains available.
5. Choose **Test backup**, select the file, enter a wrong password, then
retry with the correct password.
6. Confirm success identifies whether the backup matches the current
identity and displays only the public `npub`.

## Screenshots

| Settings identity | Private-key menu | Create backup |
|---|---|---|
| <img width="1280" height="720" alt="image"
src="https://github.com/user-attachments/assets/981e391b-6829-4081-95ca-ca75a369de71"
/> | <img width="1280" height="720" alt="image"
src="https://github.com/user-attachments/assets/7972c68e-7635-47d8-b0ad-9639390d3e6c"
/> | <img width="1280" height="720" alt="image"
src="https://github.com/user-attachments/assets/4709c8f7-cf02-46f1-bec9-b3f98fe56fb2"
/> |

| Encrypting | Download available | Test success |
|---|---|---|
| <img width="1280" height="720" alt="image"
src="https://github.com/user-attachments/assets/1ac3e934-2b4b-4135-bae6-126c715c8c59"
/> | <img width="1280" height="720" alt="image"
src="https://github.com/user-attachments/assets/cb6f07ee-a16f-44a5-b9a0-6b9fe0e4d40d"
/> | <img width="1280" height="720" alt="image"
src="https://github.com/user-attachments/assets/ea58b1b1-966c-46aa-8d59-92c9f06a25bd"
/> |

Visual review and additional states: [Buzz
thread](buzz://message?channel=50ca7ef1-201e-4159-9499-40de3964b7c3&id=87eceb5f0f82fd50c32e560de3d35be48e293760f6620718aafdcef289d475fe)

---------

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz>
2026-07-30 11:15:39 -07:00
Wes
61b96c9828 fix(catalog): update Amp description (#3758)
## Summary
- replace Amp's outdated Sourcegraph attribution in the runtime catalog
- describe Amp neutrally as a coding agent for the terminal and editor

## Verification
- `pnpm test` (desktop: 3,819 passed)
- `pnpm typecheck`
- pre-push `desktop-check`, `desktop-test`, and `branch-skew` hooks

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
2026-07-30 12:54:13 -04:00
David Hamilton
7fb008f934 fix(desktop): explain open agent access (#2561)
## Why

Hack-day feedback exposed a dangerous mismatch between the UI and the
underlying access model. The `Anyone` respond-to mode appeared as a
neutral dropdown choice, while a Buzz agent may act with the files,
accounts, and tools available on the machine where it runs.

People reasonably read this as sharing a bot in a channel. The current
UI did not explain that it can also share the agent's available access.

## What

- Reframes `respond-to` as **agent access** in user-facing UI.
- Uses plain audience labels: **Only me**, **Anyone**, and **Selected
people**.
- Warns for **both** sharing modes, not just `Anyone` — `Selected
people` also hands host access to someone other than the owner, so only
the audience phrase differs:
> Anyone can use this agent to access your computer, including files,
accounts, and connected tools.

> Selected people can use this agent to access your computer, including
files, accounts, and connected tools.
- Names the machine the agent actually runs on. A provider-backed
(remote) agent reads:
> Anyone can use this agent to access the server it runs on, including
any accounts and tools available there.

The remote wording deliberately omits the owner's files — those aren't
theirs to describe on a host they don't own.
- Places the warning below the selector for `Anyone`, but **after** the
people picker for `Selected people`, so it never sits between the user
and the selection they came to make.
- Removes Nostr, harness, pubkey, and `!shutdown` jargon from the
primary decision copy. Direct pubkey entry remains available as an
advanced path.
- Replaces the green open-access avatar dot with an amber warning marker
and accessible text. Selected access uses a separate blue status.
- Aligns the sidebar action and profile field with the same language.
- Records the shared-field disclosure contract in
`desktop/src/features/agents/AGENTS.md` so future surfaces do not
silently omit it.

## Design decisions

**Persistent inline warning, not a confirmation modal.** The setting
does not autosave; the consequence remains visible beside the selection
until the person chooses **Save access**. This gives the information
before commitment without adding a dismiss-and-confirm ritual that would
repeat in every create/edit surface.

**An unknown run location falls back to the local wording.** It does not
hedge with "computer or server". A remote host requires an installed
`buzz-backend-*` provider, and without one `WhereToRunSection` never
renders — so "server" would name a concept the owner has never been
shown. When it *is* remote, they picked that host from the selector
themselves. Surfaces never synthesize a run location they don't have.

**One resolution site, published through context.** `AgentDialog`
resolves the run location (`runLocationForBackend` from
`ManagedAgent.backend`, `runLocationForRunOn` from the create flow's
`WhereToRunDraft`) and publishes it via `AgentRunLocationContext`. It is
not threaded as a prop through `AgentDefinitionDialog` (1047 lines) or
`AgentInstanceEditDialog` (1228 lines) — neither uses the value, and
both are already over the file-size ceiling. Surfaces outside that tree
(`EditRespondToDialog`) pass the prop directly.

The copy follows the writing system's guidance for high-sensitivity
decisions: lead with the material consequence, use plain actor/action
language, keep helper text adjacent and persistent, and never rely on
color alone.

## Scope

Desktop only. The web and mobile clients do not currently expose this
setting. No protocol, gate, runtime, persistence, or backend behavior
changes.

This does not add team-scoped remote agents. It makes the current
local-or-remote access model honest while that product work remains
separate.

## Validation

- `pnpm exec biome check` and `pnpm exec tsc --noEmit` — clean
- `lib/agentAccessWarning.test.mjs` (8/8) — every mode × run-location
copy variant, both resolvers, unknown-reads-as-local, blank `runOn` is
not a provider
- `ui/respondToFieldContract.test.mjs` (8/8) — plain labels, both
warning positions, source-order guard that the `allowlist` warning
follows the picker, helper-not-inline-copy guard
- `agent-access-warning.spec.ts` (3/3) — native local, provider-backed
remote (asserts the server sentence and *not* "your computer"),
persona-backed edit; includes a bounding-box check that the `Selected
people` warning renders below the picker

---------

Signed-off-by: David Hamilton <daveh@squareup.com>
Signed-off-by: Clay Delk <clay.delk@gmail.com>
Co-authored-by: Clay Delk <clay.delk@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 13:32:40 +00:00
Will Pfleger
b0503d80c2 feat(desktop): add custom harness inline from agent dialogs (#3252)
Registering a custom ACP harness works today, but only from Settings →
Agents. Anyone whose first touchpoint is "New agent" has no way to
discover the custom path — the dropdown just lists the baked-in presets
plus whatever was registered earlier. This adds an inline "Add custom
harness…" entry to the harness dropdown in all three agent surfaces:
create, edit-definition (`AgentDefinitionDialog`), and instance edit
(`AgentInstanceEditDialog`).

The entry is a sentinel option (`ADD_CUSTOM_HARNESS_VALUE`, NUL-prefixed
so it can never collide with a real harness id — backend ids match
`[a-z0-9_][a-z0-9_-]*`), mirroring the `CUSTOM_ENTRY_ID` trick already
used in `HarnessCatalogDialog`. Picking it never writes into form state;
it opens `AddCustomHarnessDialog`, a thin modal wrapper hosting the
existing `CustomHarnessForm` in `chromeless` mode. `CustomHarnessForm`'s
`onSaved` now carries the saved `definition.id` (the form may rewrite
it); the two existing call sites ignore the argument, so their behavior
is unchanged.

Selection after save is deferred rather than immediate.
`usePendingHarnessSelection` holds the saved id until the runtime
catalog actually publishes it via discovery, then selects it exactly
once — so the dialog never selects an id it cannot render, and
back-to-back registrations resolve correctly. The wait is scoped to the
owning dialog's `open` state: both host dialogs stay mounted when
closed, so an unpublished id is dropped on close rather than selecting
into reset form state when discovery later catches up. Selection is
routed through each dialog's normal dropdown change handler, so
provider/model reset (and command pinning in the instance dialog) behave
identically to a hand-picked harness. Dismissing the modal leaves the
previous selection untouched. `AgentInstanceEditDialog`'s existing
"Custom command" option is a different feature (ad-hoc command override
vs. a registered reusable harness) and is untouched.

Coverage is 16 unit tests in `addCustomHarness.test.mjs` (real React
mount, following the existing `.test.mjs` pattern) plus 4 Playwright
specs in `inline-custom-harness.spec.ts` covering all three surfaces
end-to-end. Both suites were mutation-verified: treating the sentinel as
a real selection, selecting before the catalog publishes, never clearing
the pending id, ignoring the dialog's open state, and reversing
latest-save-wins each turn the unit tests red; reverting the two dialog
diffs turns all four e2e specs red. The `check-file-sizes.mjs` overrides
for the two dialogs are ratcheted to their exact new counts (1048 and
1229) — verified tight in both directions, N passes and N−1 fails, so no
headroom is introduced.

---------

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-28 16:01:41 -04:00
klopez4212
a35771fc44 feat(desktop): refine agent catalog sharing (#2439)
## Summary

- add custom-agent catalog sharing and hide built-ins from discovery
- let owners publish later catalog updates from Share or while saving
edits — the save always persists locally, and the publish reports
`published` or `queued` (flushed automatically once the relay is
reachable again)
- preserve agent type, model, and runtime across snapshot import/export
- simplify agent and team entry points and tighten catalog layout
- migrate the legacy global retention queue into the owner's active
scope so pending catalog publishes survive the upgrade
- keep the agent list and edits usable in recovery mode by degrading to
unshared projections when scope resolution or the retention DB fails
- track catalog provenance on copied personas, so adding an
already-added foreign agent resolves to the existing copy instead of
creating a duplicate
- scope inbound persona events to the community relay they arrived on
- page the catalog read past the relay's 1,000-row query clamp
- unify the share dialog's memory-level choice into a single "What's
included" selector that drives both DM-send and copy-link delivery (all
six combinations preserved), group the delivery rows above the option
rows, and label the catalog toggle "Not shared" / "Shared"
- keep emoji avatars on catalog entries — they persist as inline
percent-encoded SVG, which the catalog projection's http(s)-only URL
guard used to drop, so a shared agent showed initials instead of its
avatar
- drop the "Active in communities" card from Agents settings, superseded
by the per-channel runtime controls in the members sidebar

## Screenshots

### Agent actions

![Agent
actions](https://raw.githubusercontent.com/block/buzz/4643581cd0882d8b101b04e3d8be290ea7e39f08/pr-2439--01-agent-menu.png)

### Team avatar stack

![Team avatar
stack](https://raw.githubusercontent.com/block/buzz/4643581cd0882d8b101b04e3d8be290ea7e39f08/pr-2439--02-team-menu.png)

### Catalog sharing

![Catalog
sharing](https://raw.githubusercontent.com/block/buzz/648d6eaf6df7d4daa5b7375948ed221f723793d6/pr-2439--03-share-to-catalog.png)

### Publish while editing

![Publish while
editing](https://raw.githubusercontent.com/block/buzz/bd4eb473ff456f6e665173054dc5a0f764594d1e/pr-2439--01-edit-agent-publish-updates.png)

### Publish from Share

![Publish from
Share](https://raw.githubusercontent.com/block/buzz/648d6eaf6df7d4daa5b7375948ed221f723793d6/pr-2439--02-share-dialog-publish-updates.png)

### Catalog details

![Catalog
details](https://raw.githubusercontent.com/block/buzz/4643581cd0882d8b101b04e3d8be290ea7e39f08/pr-2439--04-agent-catalog.png)

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-28 15:01:03 -04:00
Taylor Ho
d98da7389e feat(desktop): redesign agent runtime settings (#3093)
**Category:** improvement
**User Impact:** Users can understand, install, authenticate, and manage
agent runtimes from one progressively disclosed Agents settings
experience.

**Problem:** Runtime health and custom harness management were split
across overlapping settings surfaces, exposing low-level configuration
too early while leaving setup and authentication states hard to
understand. **Solution:** Consolidate those operations into one stable
runtime list and an Add runtimes catalog, with task-oriented state
labels, direct setup actions, and custom configuration contained in a
dedicated form.

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

**desktop/playwright.config.ts**
Registers the visual coverage needed for the redesigned runtime catalog.

**desktop/public/harness-logos/CREDITS.md**
Documents bundled runtime-mark provenance and the decision not to ship
the withdrawn OpenAI mark.

**desktop/public/runtime-icons/codex.png**
Removes the obsolete Codex bitmap in favor of the neutral fallback.

**desktop/public/runtime-icons/goose.svg**
Removes the old Goose asset now replaced by the theme-adaptive mark.

**desktop/src-tauri/src/managed_agents/discovery.rs**
Aligns runtime discovery guidance with the new task-oriented setup
language.

**desktop/src-tauri/src/managed_agents/discovery/runtime_metadata.rs**
Updates runtime metadata used by the redesigned settings states.

**desktop/src/features/agents/ui/runtimeAvailabilityWarning.test.mjs**
Updates availability-warning expectations for the revised runtime
guidance.

**desktop/src/features/onboarding/assets/harness-logos/chatgpt.png**
Removes the redundant bitmap from the unified runtime icon pipeline.

**desktop/src/features/onboarding/assets/harness-logos/goose.png**
Removes the redundant Goose bitmap.

**desktop/src/features/onboarding/ui/HarnessMarks.tsx**
Adds theme-adaptive bundled runtime marks with safe fallbacks.

**desktop/src/features/onboarding/ui/RuntimeIcon.tsx**
Centralizes runtime logo rendering so settings and catalog rows cannot
drift.

**desktop/src/features/onboarding/ui/SetupStep.tsx**
Aligns onboarding runtime setup copy with the settings terminology.

**desktop/src/features/onboarding/ui/presetLogos.test.mjs**
Guards bundled-logo behavior and prevents the withdrawn Codex mark from
returning.

**desktop/src/features/settings/ui/CustomHarnessForm.tsx**
Reworks custom runtime creation and editing into a clear, dedicated
catalog form.

**desktop/src/features/settings/ui/HarnessCatalogDialog.tsx**
Introduces the Add runtimes master-detail catalog, grouped setup states,
loading treatment, and pinned actions.

**desktop/src/features/settings/ui/HarnessManagementCard.tsx**
Removes the superseded standalone custom-harness management surface.

**desktop/src/features/settings/ui/HarnessRow.tsx**
Provides stable operational runtime rows with install, update,
authentication, edit, and delete behavior.

**desktop/src/features/settings/ui/HarnessesSettingsPanel.tsx**
Consolidates runtime health and custom management into one Agents
settings panel.

**desktop/src/features/settings/ui/SettingsPanels.tsx**
Wires the consolidated panel into Agents settings.

**desktop/src/features/settings/ui/harnessCatalogCopy.ts**
Adds restrained, source-annotated runtime descriptions and setup
guidance.

**desktop/src/features/settings/ui/harnessCatalogLogic.test.mjs**
Covers grouping, state labels, stable row order, actions, and adapter
warnings.

**desktop/src/features/settings/ui/harnessCatalogLogic.ts**
Centralizes catalog grouping, actions, status labels, and runtime-safe
warning copy.

**desktop/src/features/settings/ui/harnessGalleryLogic.test.mjs**
Removes obsolete gallery-only tests after consolidation.

**desktop/src/features/settings/ui/harnessGalleryLogic.ts**
Retains only the shared custom-runtime safety logic needed by the new
surface.

**desktop/src/shared/ui/config-nudge-attachment.tsx**
Points configuration nudges to Agent runtimes with matching terminology.

**desktop/src/testing/e2eBridge.ts**
Adds deterministic runtime states for authentication and catalog E2E
coverage.

**desktop/tests/e2e/doctor-states.spec.ts**
Verifies ready, setup-required, node-gated, authentication, loading, and
error contracts.

**desktop/tests/e2e/harness-catalog-screenshots.spec.ts**
Captures whole-pane visual states for the runtime catalog experience.

**desktop/tests/e2e/harness-management.spec.ts**
Exercises catalog actions and custom runtime create, edit,
authentication, and deletion flows.

**desktop/tests/e2e/onboarding-agent-defaults.spec.ts**
Updates default-runtime expectations for the consolidated experience.

**desktop/tests/e2e/profile.spec.ts**
Aligns profile navigation assertions with the new settings surface.

</details>

## Reproduction steps

1. Open Settings → Agents and inspect Agent runtimes; ready, signed-out,
installable, and setup-required runtimes should have stable rows and
explicit actions.
2. Open Add runtimes and browse the Setup and Installed groups; select
entries to see sourced guidance and a pinned Install or Setup guide
action.
3. Select Custom harness, create a runtime, then edit and delete it;
verify required-field gating and the blast-radius confirmation.
4. Exercise a signed-out runtime and connect it; the row should move
from Sign-in needed to Ready without reordering.
5. Resize the window and switch themes to verify responsive layout and
adaptive bundled marks.

## Screenshots

| Mixed runtime states | Goose not installed | Sign-in needed |
|---|---|---|
| <img width="980" height="1000" alt="image"
src="https://github.com/user-attachments/assets/28705b39-1d91-440a-a954-fb2d7d8ee759"
/> | <img width="980" height="1000" alt="image"
src="https://github.com/user-attachments/assets/e65ce8e1-8c85-4272-afb5-e6001b94d560"
/> | <img width="980" height="1000" alt="image"
src="https://github.com/user-attachments/assets/3987f355-960d-4744-8c26-b3746944a33c"
/> |

| Add runtimes catalog | Custom runtime | Setup guide |
|---|---|---|
| <img width="896" height="672" alt="image"
src="https://github.com/user-attachments/assets/a2f32c8a-4dff-4929-af96-b07346933335"
/> | <img width="896" height="672" alt="image"
src="https://github.com/user-attachments/assets/b2e85a0c-3940-4875-a138-4035423c9de7"
/> | <img width="896" height="672" alt="image"
src="https://github.com/user-attachments/assets/5a690c1c-368e-4ee3-98b9-ffefdc3cf804"
/> |

Full 13-state screenshot matrix and review evidence:
https://buzz.block.builderlab.xyz/channels/c5af9e3e-4317-4853-b3e3-ed9c15bc511d?event=ec88f6e472f2b08d6318ae76fd754fd4f218385f67cfe59b73034c4bc34c9252

---------

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz>
Co-authored-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz>
2026-07-27 11:01:42 -07:00
Will Pfleger
95fdf97880 feat(acp): bring your own harness (BYOH) — generic ACP runtime seam + settings gallery (#2773)
## What

Implements a "bring your own harness" (BYOH) generic ACP mechanism —
replacing per-harness backend code with a data-driven 3-tier system:

- **Tier 1 (compiled-in builtins):** goose, claude, codex, buzz-agent —
unchanged behavior
- **Tier 2 (bundled presets):** cursor, omp, grok, opencode, kimi, amp,
hermes, openclaw, and any future additions — defined in
`PRESET_HARNESSES`, no code duplication, icons stay
TerminalSquare/bundled-asset-only
- **Tier 3 (user-defined custom):** JSON definitions saved to
`custom_harnesses/` under app data; managed via Settings → Agents UI

## Changes

### Core data model
- `HarnessDefinition` — id, label, command, args, env, install URL/hint
- `PRESET_HARNESSES` static table — single source of truth for all
presets; `preset_harness_ids()` derives reserved IDs (D-11: no
hand-maintained copy)
- `source: "builtin" | "preset" | "custom"` tagging on every catalog
entry

### Persistence (B-4, B-6)
- `save_custom_harness_to_dir(dir, definition, rename_old_id)` —
backup-swap atomic write (backs up target → .bak, commits temp → target,
restores .bak on failure, removes .bak on success); safe on Windows
where `fs::rename` over an existing file is "access denied"
- `save_and_warm` / `delete_and_warm` — hold `PERSIST_MUTEX` for the
write + registry-warm pair, eliminating the lost-update race (B-6) where
two concurrent saves could interleave their warm calls and leave a stale
registry snapshot
- Validate-before-mutate: both IDs and env validated before any
filesystem mutation

### Env validation boundary (B-3)
- `validate_harness_definition_pub` calls `validate_user_env_keys` on
definition env at save AND load
- Rejects malformed keys (BUZZ_AUTH_TAG=x forgery shape), reserved keys
(BUZZ_MANAGED_AGENT etc.), NUL bytes, oversized values

### TypeScript boundary (B-2 / Thufir CRITICAL)
- `RawAcpRuntimeCatalogEntry` now declares `definition_env?:
Record<string,string>` and `source: "builtin" | "preset" | "custom"`
- `fromRawAcpRuntimeCatalogEntry` maps `definition_env → definitionEnv`
(camelCase); absent field defaults to `{}`
- Edit form reads `entry.definitionEnv` — env no longer erased on
save-then-edit cycle

### Unified descriptor (Phase A / Thufir F4)
- `EffectiveHarnessDescriptor { command, args, env }` in `readiness.rs`
- `resolve_effective_harness_descriptor()` — single resolver used by
spawn, spawn_hash, summary, get_agent_models (both saved and unsaved),
and readiness
- No competing arg-resolution forms

### Other fixes
- B-5: stop freezing `runtime.defaultArgs` into `record.agent_args` on
normal create paths
- B-7: readiness exec-check — `MissingBinary` variant for custom
commands not found on PATH
- B-8: onboarding transition — `setTimeout(0)` removed, parent-owned
route intent via `navigateAfterComplete` prop
- C-9: collector-discriminating sweep tests with injectable filters
- C-10: `HarnessManagementCard` uses `harnessGalleryLogic` helpers
(killed duplicate filter/sort)
- D-11: `BUILTIN_IDS` derived from `PRESET_HARNESSES` (no
hand-maintained copy)
- D-12: `mobile/pubspec.lock` churn reverted
- D-13: false ownership fast-path comment fixed
- D-14: URL scheme validation for `installInstructionsUrl`
- D-15: OpenClaw Gateway env-locus README line

### Tests added
**B-4 persistence (6 tests):**
`save_to_dir_create_writes_file_and_loads_back`,
`save_to_dir_same_id_edit_replaces_content`,
`save_to_dir_backup_is_cleaned_up_after_same_id_edit`,
`save_to_dir_rename_removes_old_file_and_creates_new`,
`save_to_dir_rename_nonexistent_old_id_is_non_fatal`,
`save_to_dir_roundtrip_with_env_preserves_values`

**B-3 env validation (6 tests):**
`validate_rejects_malformed_key_with_equals_sign`,
`validate_rejects_reserved_key_buzz_managed_agent`,
`validate_rejects_reserved_key_case_insensitive`,
`validate_rejects_nul_byte_in_value`,
`validate_rejects_value_over_per_value_size_limit`,
`validate_accepts_well_formed_env`

**B-2 API boundary (4 TS tests in tauri.test.mjs):**
`fromRawAcpRuntimeCatalogEntry maps definition_env to definitionEnv`,
`defaults definitionEnv to {} when absent`, `preserves source preset`,
`env round-trips through edit payload shape`

## Preset catalog

| ID | Label | Command |
|----|-------|---------|
| `cursor` | Cursor | `cursor-agent acp` |
| `omp` | Oh My Pi | `omp acp` |
| `grok` | Grok Build | `grok agent --always-approve stdio` |
| `opencode` | OpenCode | `opencode acp` |
| `kimi` | Kimi Code | `kimi acp` |
| `amp` | Amp | `amp-acp` |
| `hermes` | Hermes Agent | `hermes-acp` |
| `openclaw` | OpenClaw | `openclaw acp` |

## Review-fix pass (2026-07-26, Eva)

Fixes from the three-way review (Wren / Dawn / Eva) in the
buzz-generic-acp-harnesses thread, pushed as new commits (no rewrite):

1. **installHint edit round-trip** — form seeding extracted to
`formValuesFromCatalogEntry` (single source of truth), input rendered,
full-definition lossless round-trip regression.
2. **Dangling-delete coherence** — delete allowed; confirm counts
referencing agents (direct pin + persona-inherited); summary rows render
`harness (deleted): <id>`; spawn errors become actionable sentences
(`user_facing_harness_error`); composed delete→summary→start test.
3. **Comma-in-args** — rejected at `validate_harness_definition` (shared
by save AND disk load), mirrored inline in the form.
4. **Registry publish race** — collision/dup filtering moved into
`load_custom_harnesses` (both loaders inherit shadowing rules);
discovery publishes by re-reading the dir under `persist_mutex` (lock
scoped to publish only); deterministic interleaving regressions for
save-during-discovery and delete-during-discovery.
5. **Mechanical** — discarded `belongs_to_us` sweep arg deleted,
`load_global_agent_config` hoisted out of the per-record summary loop,
duplicated doc paragraph + stray SAFETY comment removed.
6. **PGID test de-flaked** — leader kept alive through the assertion.

Known follow-up (filed in review, not blocking): file-size split-outs
queued in `check-file-sizes.mjs` entries.

## Gate table — head `bf53f1d60`

| Gate | Result |
|------|--------|
| `cargo test --lib` (desktop/src-tauri) | **1701 passed**, 0 failed, 14
ignored |
| desktop JS suite (`pnpm test`) | **3605 passed**, 0 failed |
| `tsc --noEmit` | clean |
| `biome check` + file-size/px/pubkey checks | clean |
| `cargo clippy --lib -- -D warnings` | clean |
| `cargo fmt --check` | clean |

PR head: `bf53f1d60e3cbd07392e1287b83bb37ba90d0d33` — includes merge of
origin/main (`c2a4ee711`, conflicts in agent_models composed with
#2890's live Databricks discovery)

---------

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
Co-authored-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com>
Co-authored-by: Dawn (sprout agent) <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
2026-07-26 20:06:20 -04:00
klopez4212
e6c90bb7c4 Polish community rail and mobile pairing (#2972)
## Summary
- align the multi-community rail with the content surface and balance
its visible 10px side gutters
- center Mobile pairing, start sessions on demand, and keep retry states
inside the QR area
- reveal the QR code and copy action with 250ms motion and use the
standard loading spinner

The rail was centered within its own box, but the adjacent sidebar added
another 11px to the visible right gap. Mobile pairing also started
before user intent, which could leave an idle session waiting for EOSE.

## Validation
- `pnpm -C desktop build:e2e`
- `pnpm -C desktop test` — 3,516 passed
- `pnpm -C desktop exec playwright test tests/e2e/community-rail.spec.ts
--project=smoke` — 19 passed
- `pnpm -C desktop exec playwright test
tests/e2e/mobile-pairing-qr.spec.ts --project=smoke` — 1 passed

`pnpm -C desktop check` is currently blocked by the existing
`src-tauri/src/managed_agents/runtime.rs` file-size baseline (2,220
lines; limit 2,216).
2026-07-26 13:20:26 -07:00
klopez4212
50655ac097 Style mobile pairing QR codes (#2775)
## Summary
- render mobile pairing QR codes locally with Wallet-inspired styling
- hide the raw pairing URI behind a matching copy button
- add focused rendering and dialog coverage

## Test plan
- desktop lint and 3,489 desktop tests
- focused pairing dialog Playwright test
- decoded the rendered QR back to the exact pairing URI
2026-07-24 19:46:59 -07:00
klopez4212
384c72dee6 Refine community management flows (#2738)
## Summary
- simplify Add community into clear create and join paths
- align community actions and icon editing across the rail, profile
menu, and settings
- consolidate invites around the member list and a reusable link dialog

## Testing
- `pnpm check`
- `pnpm test` (3,483 passed)
- `pnpm run build:e2e`
- focused Playwright coverage (34 passed)

## Snapshots

| Add a community | Join an existing community |
| --- | --- |
| ![Add a
community](https://raw.githubusercontent.com/block/buzz/fae692faa77186df7702fb025ca3082c7cc93a8c/pr-2738--01-add-community.png)
| ![Join an existing
community](https://raw.githubusercontent.com/block/buzz/fae692faa77186df7702fb025ca3082c7cc93a8c/pr-2738--02-join-community.png)
|

| Create a new community | Hosted community icon |
| --- | --- |
| ![Create a new
community](https://raw.githubusercontent.com/block/buzz/fae692faa77186df7702fb025ca3082c7cc93a8c/pr-2738--03-create-community.png)
| ![Hosted community
icon](https://raw.githubusercontent.com/block/buzz/fae692faa77186df7702fb025ca3082c7cc93a8c/pr-2738--04-community-icon.png)
|

| Invites | Invite link |
| --- | --- |
|
![Invites](https://raw.githubusercontent.com/block/buzz/fae692faa77186df7702fb025ca3082c7cc93a8c/pr-2738--05-invites.png)
| ![Invite
link](https://raw.githubusercontent.com/block/buzz/fae692faa77186df7702fb025ca3082c7cc93a8c/pr-2738--06-invite-link.png)
|

---------

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>
2026-07-24 19:30:33 -07:00
Atish Patel
b8510ede1b fix(desktop): clarify CLI runtime setup (#2680)
## Why

Installing the Codex, Claude, or Goose desktop app does not install the
command-line harness Buzz needs. The current UI makes that distinction
unclear, links some missing-CLI states to adapter documentation, and can
report a successful install from the installer exit code even when
runtime discovery still fails. On Windows, Buzz also invokes Goose's
Bash installer, which writes the executable somewhere Buzz does not
discover.

## What

- distinguish missing vendor CLIs from missing or outdated ACP adapters
in runtime metadata and UI guidance
- link Codex, Claude Code, and Goose missing-CLI states to their
official CLI installation documentation
- explain in Settings, onboarding, and agent configuration that the
desktop app alone is not sufficient
- use Goose's official PowerShell installer on Windows
- refresh PATH and rediscover the requested runtime after installation,
keeping the control retryable if the runtime is still unavailable
- add Rust and Playwright regression coverage for Windows installer
selection, CLI/adapter guidance, false-success prevention, verified
installs, and onboarding copy

## Risk Assessment

Medium. This changes desktop onboarding and runtime installation
behavior. Successful installs now require the runtime catalog to verify
availability; previously hidden discovery failures will surface as
actionable errors instead of a false success state.

## References

- [Codex CLI installation](https://developers.openai.com/codex/cli/)
- [Claude Code
installation](https://code.claude.com/docs/en/getting-started)
- [Goose
installation](https://goose-docs.ai/docs/getting-started/installation/)
- Follow-up to #2563 and #2587

## Validation

- `just desktop-typecheck`
- `just desktop-test` — 3,455 passed
- focused Rust post-install verification tests
- focused Playwright Doctor/onboarding coverage (in progress; CI and
local sequential rerun will provide final results)

Generated with Codex

---------

Signed-off-by: Atish Patel <atish@squareup.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Goose <opensource@block.xyz>
2026-07-24 20:21:25 -04:00
Will Pfleger
9731cd818b fix(onboarding): show real install errors and fix concurrent install state (#2658)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
2026-07-24 12:12:08 -04:00
Wes
e341b09cb9 fix: expose community icon control on open relays (#2640)
Signed-off-by: npub1cl47vfhsqpqy9pwndphpm36vcp7vvz5h2js4qpqm5yewzj7nutkq7xyw8c <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Co-authored-by: npub1cl47vfhsqpqy9pwndphpm36vcp7vvz5h2js4qpqm5yewzj7nutkq7xyw8c <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
2026-07-23 16:13:48 -07:00
Wes
fd967c6ea4 feat(desktop): gate sign-out behind key backup + typed confirmation (#2424)
Signed-off-by: Wes <wesb@block.xyz>
Co-authored-by: npub1cl47vfhsqpqy9pwndphpm36vcp7vvz5h2js4qpqm5yewzj7nutkq7xyw8c <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
2026-07-22 15:38:52 -07:00
Tyler
61cc738ee8 feat(desktop+acp): spawn a harness per (agent, community) pair at GUI startup — warm sockets, lazy LLM pool (#2122)
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: Tyler <109685178+tlongwell-block@users.noreply.github.com>
Co-authored-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1t2tgm7d8f995uqvmnm8h88sg3wnpp9a5xysjf6dg3tjmgt3ltulqdp8ehr <5a968df9a7494b4e019b9ecf739e088ba61097b4312124e9a88ae5b42e3f5f3e@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: Dawn <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: npub1hwqy0rnujtl25dzmlhn8qwux4kr8sjhas3ugltx9j5dm5dwkp2dsqjhytw <bb80478e7c92feaa345bfde6703b86ad86784afd84788facc5951bba35d60a9b@buzz.block.builderlab.xyz>
2026-07-22 13:24:46 -07:00
Wes
6d04d279ae Show team count separately in channel template rows (#2404)
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Rick <738ae2df545ecddd7788884357e8ab6842c5b10389b980a58aad43b424db74d9@sprout-oss.stage.blox.sqprod.co>
2026-07-22 11:27:25 -07:00
Will Pfleger
fd55ab6624 fix(desktop): keep machine onboarding for unrecognized identities after reset (#2244) 2026-07-21 15:24:35 -04:00
Bradley Axen
e315c4982f fix(desktop): clarify data deletion action (#2208)
Signed-off-by: npub1rf6fvdj6ut0c4kcmjv4p5mmgh89nj58n69uu3fz3cvk3jn500hqs7emz79 <1a7496365ae2df8adb1b932a1a6f68b9cb3950f3d179c8a451c32d194e8f7dc1@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1rf6fvdj6ut0c4kcmjv4p5mmgh89nj58n69uu3fz3cvk3jn500hqs7emz79 <1a7496365ae2df8adb1b932a1a6f68b9cb3950f3d179c8a451c32d194e8f7dc1@sprout-oss.stage.blox.sqprod.co>
2026-07-20 22:30:54 -07:00
morgmart
8c77a0cf8f Polish agent defaults surfaces: app dropdowns + collapsed Advanced env vars (#2184) 2026-07-20 12:00:04 -07:00
morgmart
c5db094350 refactor(desktop): rename agent config files to agent-config family (#2140) 2026-07-19 13:12:24 -07:00
Bradley Axen
cd0e7402d7 Add owned community onboarding (#2123)
Signed-off-by: npub1rf6fvdj6ut0c4kcmjv4p5mmgh89nj58n69uu3fz3cvk3jn500hqs7emz79 <1a7496365ae2df8adb1b932a1a6f68b9cb3950f3d179c8a451c32d194e8f7dc1@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1rf6fvdj6ut0c4kcmjv4p5mmgh89nj58n69uu3fz3cvk3jn500hqs7emz79 <1a7496365ae2df8adb1b932a1a6f68b9cb3950f3d179c8a451c32d194e8f7dc1@sprout-oss.stage.blox.sqprod.co>
2026-07-19 12:44:18 -07:00
klopez4212
2b0f5e9f80 Improve agent runtime settings (#2026) 2026-07-19 19:01:38 +00:00
morgmart
2d0bd6ec4a feat(desktop): add focused thread mode (#2108)
Co-authored-by: Claude <noreply@anthropic.com>
2026-07-19 09:55:39 -06:00