Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
6300a6b
fix(acp): per-runtime env defaults at spawn — isolate Hermes from con…
tlongwell-block Jul 29, 2026
485d03a
Fix mobile attachment and gallery polish (#3370)
klopez4212 Jul 29, 2026
c405ad1
feat(agent): fix Anthropic prompt caching with Databricks (+ MCP prox…
atishpatel Jul 29, 2026
ce01e93
Polish mobile typing indicator (#3528)
klopez4212 Jul 29, 2026
24d90d1
Refine community invite limits (#3529)
klopez4212 Jul 29, 2026
6438ded
feat(agent): route Claude/GPT model families to their native gateway …
atishpatel Jul 29, 2026
4555899
Polish mobile navigation and menus (#3486)
klopez4212 Jul 29, 2026
f7a3988
fix(desktop): preserve shared agent fidelity (#3553)
wesbillman Jul 29, 2026
294c8c8
perf(desktop): move observer-feed archive and decrypt commands off ma…
wpfleger96 Jul 29, 2026
51bb97d
Run Tauri clippy in pre-push (#3555)
wesbillman Jul 29, 2026
a13085e
chore(release): release Buzz Desktop version 0.5.1 (#3566)
wesbillman Jul 29, 2026
9752b81
Serialize Tauri pre-push checks (#3567)
wesbillman Jul 29, 2026
9beb3b8
fix(cli): mask credential env values in --help output (#3570)
wpfleger96 Jul 29, 2026
4a1ebf2
feat(agent): make Gemini and MLflow-route models usable through datab…
atishpatel Jul 29, 2026
ddd4687
revert(acp): remove dead GOOSE_ACP_SCHEDULER_DISABLED env injection (…
wpfleger96 Jul 29, 2026
7e9b77f
Fix inline raster avatars in agent catalog (#3581)
wesbillman Jul 29, 2026
324bd6b
Fix shared agent avatar import profiles (#3578)
wesbillman Jul 29, 2026
259de6a
Improve emoji autocomplete matching (#3571)
klopez4212 Jul 29, 2026
047533c
fix(mobile): keep TLS on relays joined by invite (#3139)
inventivepotter Jul 29, 2026
b42a8d4
fix(desktop): reconcile thread arrivals at bottom (#3585)
wesbillman Jul 29, 2026
66e7054
fix(desktop): deduplicate relay outage notification (#3579)
loganj Jul 29, 2026
7adc462
feat(cli): mirror Desktop mention delivery (#3330)
loganj Jul 29, 2026
581baa6
chore(ci): bump Linux AppImage build container to ubuntu:24.04 (#3602)
wpfleger96 Jul 29, 2026
005b5b8
feat(tracing): correlate trace IDs in relay logs (#3608)
ThePumpingLemma Jul 29, 2026
5aeed7c
fix(desktop): discover bun-installed agent CLIs in ~/.bun/bin (#3343)
linxule Jul 29, 2026
b18e559
docs: add Linux rendering troubleshooting guide (#3573)
wpfleger96 Jul 29, 2026
3e48f1b
chore(release): release Buzz Desktop version 0.5.2 (#3624)
wesbillman Jul 29, 2026
f95fdc1
feat(agent,acp): wire provider total_tokens through NIP-AM publish ch…
wpfleger96 Jul 29, 2026
ab55fee
feat: add first-class OpenRouter provider support (#1975)
wpfleger96 Jul 29, 2026
7012d86
feat: configure S3 URL addressing style (#3400)
kalvinnchau Jul 30, 2026
788b3c0
fix(git): channel binding tooling + author remediation for unbound re…
tlongwell-block Jul 30, 2026
63496cc
feat(replica): portable heartbeat-token fence with snapshot-local rea…
tlongwell-block Jul 30, 2026
bd94058
Merge remote-tracking branch 'upstream/main' into kannaka-sync-upstream
flaukowski Jul 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,19 @@ RELAY_URL=ws://localhost:3000
# BUZZ_GIT_PACK_CACHE_MAX_BYTES=5368709120
# BUZZ_GIT_PACK_CACHE_MAX_CONCURRENT_POPULATIONS=2

# -----------------------------------------------------------------------------
# S3-Compatible Object Storage (media + Git/CAS)
# -----------------------------------------------------------------------------
# The local MinIO container is reachable from host processes at localhost:9000.
# Path style keeps the bucket in the URL path and is required by this local DNS
# setup. Use `virtual` only when the provider requires bucket-as-subdomain URLs.
BUZZ_S3_ENDPOINT=http://localhost:9000
BUZZ_S3_ACCESS_KEY=buzz_dev
BUZZ_S3_SECRET_KEY=buzz_dev_secret
BUZZ_S3_BUCKET=buzz-media
BUZZ_S3_REGION=us-east-1
BUZZ_S3_ADDRESSING_STYLE=path

# -----------------------------------------------------------------------------
# Media Upload Admission
# -----------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/linux-canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
name: Build Linux canary
if: github.repository == 'block/buzz'
runs-on: ubuntu-latest
container: ubuntu:22.04@sha256:0e0a0fc6d18feda9db1590da249ac93e8d5abfea8f4c3c0c849ce512b5ef8982
container: ubuntu:24.04@sha256:4fbb8e6a8395de5a7550b33509421a2bafbc0aab6c06ba2cef9ebffbc7092d90
timeout-minutes: 60
permissions:
contents: read
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,7 @@ jobs:
if: github.repository == 'block/buzz'
runs-on: ubuntu-latest
# Digest-pinned like the SHA-pinned actions below; Renovate keeps it fresh.
container: ubuntu:22.04@sha256:0e0a0fc6d18feda9db1590da249ac93e8d5abfea8f4c3c0c849ce512b5ef8982
container: ubuntu:24.04@sha256:4fbb8e6a8395de5a7550b33509421a2bafbc0aab6c06ba2cef9ebffbc7092d90
needs: setup
timeout-minutes: 60
permissions:
Expand All @@ -508,7 +508,7 @@ jobs:
env:
DEBIAN_FRONTEND: noninteractive
run: |
# Must run first: bare ubuntu:22.04 ships without curl, wget, git, or
# Must run first: bare ubuntu:24.04 ships without curl, wget, git, or
# ca-certificates. activate-hermit bootstraps via curl+HTTPS (needs
# both), and actions/checkout falls back to a REST tarball without git.
# Running as root — no sudo needed.
Expand Down
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,38 @@
# Changelog

## v0.5.2

- feat(cli): mirror Desktop mention delivery ([#3330](https://github.com/block/buzz/pull/3330)) ([`7adc46268`](https://github.com/block/buzz/commit/7adc46268d5e93f0b1d4dc8e700af22815dcac1b))
- fix(desktop): deduplicate relay outage notification ([#3579](https://github.com/block/buzz/pull/3579)) ([`66e705492`](https://github.com/block/buzz/commit/66e7054928cc29395f828467c3e8c81b7408dd29))
- fix(desktop): reconcile thread arrivals at bottom ([#3585](https://github.com/block/buzz/pull/3585)) ([`b42a8d447`](https://github.com/block/buzz/commit/b42a8d447e3a2b85b2313dc4fdd123731fd8bba3))
- Improve emoji autocomplete matching ([#3571](https://github.com/block/buzz/pull/3571)) ([`259de6afb`](https://github.com/block/buzz/commit/259de6afbe0cc0d106e57ebdb2323064990e4122))
- Fix shared agent avatar import profiles ([#3578](https://github.com/block/buzz/pull/3578)) ([`324bd6b46`](https://github.com/block/buzz/commit/324bd6b464de5751e12abbd155376046ce3d2afc))
- Fix inline raster avatars in agent catalog ([#3581](https://github.com/block/buzz/pull/3581)) ([`7e9b77f72`](https://github.com/block/buzz/commit/7e9b77f72d82e019a99f074f1c9829be30c57ae1))
- feat(agent): make Gemini and MLflow-route models usable through databricks_v2 ([#3569](https://github.com/block/buzz/pull/3569)) ([`4a1ebf25c`](https://github.com/block/buzz/commit/4a1ebf25c782fc6a68f0a69e6f866f793a259a1f))


## v0.5.1

- perf(desktop): move observer-feed archive and decrypt commands off main thread ([#3415](https://github.com/block/buzz/pull/3415)) ([`294c8c821`](https://github.com/block/buzz/commit/294c8c821de51442a8c384c0bdb66b1a10224ca0))
- fix(desktop): preserve shared agent fidelity ([#3553](https://github.com/block/buzz/pull/3553)) ([`f7a3988ba`](https://github.com/block/buzz/commit/f7a3988ba13b590d9a55a7e8413fc3fb5ffbef18))
- feat(agent): route Claude/GPT model families to their native gateway wire ([#3538](https://github.com/block/buzz/pull/3538)) ([`6438dedf8`](https://github.com/block/buzz/commit/6438dedf83a9dbe1853e484326911bf6c7f1618c))
- Refine community invite limits ([#3529](https://github.com/block/buzz/pull/3529)) ([`24d90d128`](https://github.com/block/buzz/commit/24d90d1280a9325c6cbcf8eea30ac54db5afd2cb))
- feat(agent): fix Anthropic prompt caching with Databricks (+ MCP proxy/TLS passthrough) ([#3463](https://github.com/block/buzz/pull/3463)) ([`c405ad1d4`](https://github.com/block/buzz/commit/c405ad1d4b1da061c11b3d26761252d41dcc62d3))
- feat: add explicit entry for claude-opus-5 in model config ([#2831](https://github.com/block/buzz/pull/2831)) ([`90e058ebf`](https://github.com/block/buzz/commit/90e058ebf68137e048a409aec6616519379ff726))
- fix(desktop): clear stale thread new-message pill ([#3411](https://github.com/block/buzz/pull/3411)) ([`55a3ed7b9`](https://github.com/block/buzz/commit/55a3ed7b9217cee5b23e0a5441947dc929b2a38c))
- fix(ci): ratchet file sizes against the base tree ([#3352](https://github.com/block/buzz/pull/3352)) ([`9227bdf58`](https://github.com/block/buzz/commit/9227bdf58ad6664ae3c1078888f2181ec19c4da4))
- feat(desktop): apply WebKit rendering workarounds at startup on Linux ([#3271](https://github.com/block/buzz/pull/3271)) ([`3ece4461d`](https://github.com/block/buzz/commit/3ece4461df8a7b9663a8e68327483b8377d4086d))
- fix(desktop): stabilize flaky DM expansion E2E ordering assertions ([#2004](https://github.com/block/buzz/pull/2004)) ([`913d564ce`](https://github.com/block/buzz/commit/913d564ce0f35924291bf3eeab6508517a6d8d1f))
- fix(desktop): paint community rail full height ([#3382](https://github.com/block/buzz/pull/3382)) ([`1d3b810ad`](https://github.com/block/buzz/commit/1d3b810ad70d6325718ed91e723f32c4a376d5e1))
- feat(desktop): add custom harness inline from agent dialogs ([#3252](https://github.com/block/buzz/pull/3252)) ([`b0503d80c`](https://github.com/block/buzz/commit/b0503d80c298b1ece3b0a43b41d316829a3379e7))
- feat(desktop): refine agent catalog sharing ([#2439](https://github.com/block/buzz/pull/2439)) ([`a35771fc4`](https://github.com/block/buzz/commit/a35771fc441cdc3c6f517f419037206783b502d2))
- fix(desktop): keep drafts out of the Inbox All view ([#3217](https://github.com/block/buzz/pull/3217)) ([`3afa129ee`](https://github.com/block/buzz/commit/3afa129ee785cc74d921d0ba969254a8255c4cc0))
- fix(desktop): restore the inbox icon in the sidebar ([#3341](https://github.com/block/buzz/pull/3341)) ([`00ede2e7a`](https://github.com/block/buzz/commit/00ede2e7aa7eb95571b7db3ebbd163adbf6cf74e))
- fix(desktop): gate codex-acp on a minimum supported version ([#3254](https://github.com/block/buzz/pull/3254)) ([`4e3998f36`](https://github.com/block/buzz/commit/4e3998f36e36d68b9a93dcbd85f0864450bb8f5f))
- feat(cli): add users set-status command for NIP-38 profile status ([#3253](https://github.com/block/buzz/pull/3253)) ([`60158fce3`](https://github.com/block/buzz/commit/60158fce3e670f11bb35d42627857ccaea50ff06))
- fix(composer): scope multiline block formatting ([#3246](https://github.com/block/buzz/pull/3246)) ([`5457c947a`](https://github.com/block/buzz/commit/5457c947a74f5ba4b979f9c6411aa7626a858387))


## v0.5.0

- feat(invites): add use-limited invite links ([#3141](https://github.com/block/buzz/pull/3141)) ([`d500c2d5c`](https://github.com/block/buzz/commit/d500c2d5cf5d9aabe0ca4ebebfcafdbe5f5b7fd3))
Expand Down
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

150 changes: 76 additions & 74 deletions crates/buzz-acp/src/acp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,6 @@ use crate::usage::{TurnUsage, UsageTracker};
/// Lines exceeding this limit are rejected to prevent OOM from rogue agents.
const MAX_LINE_SIZE: usize = 10_000_000; // 10 MB

/// Env var that tells a goose ACP child not to start its cron scheduler.
/// Injected unconditionally by [`AcpClient::spawn`]; see the call site for why.
pub(crate) const GOOSE_SCHEDULER_DISABLED_ENV: &str = "GOOSE_ACP_SCHEDULER_DISABLED";

/// An MCP server configuration passed to `session/new`.
///
/// Corresponds to the `McpServerStdio` variant in the ACP schema.
Expand Down Expand Up @@ -494,29 +490,29 @@ impl AcpClient {
// entry falls through to the standard operator-wins treatment below.
let codex_merge_active = codex_config_value.is_some();

// Per-runtime environment defaults (e.g. Hermes MCP-startup isolation).
// Applied first so both persona `extra_env` (below, via `Command::env`
// key replacement) and inherited parent env (via the parent-presence
// check) override them.
for &(key, value) in crate::config::default_agent_env(command) {
if std::env::var_os(key).is_none() {
cmd.env(key, value);
}
}

for (key, value) in extra_env {
if key == "CODEX_CONFIG" && codex_merge_active {
// Handled by build_codex_config_env; skip here to avoid double-setting.
continue;
}
if std::env::var(key).is_err() {
if std::env::var_os(key).is_none() {
cmd.env(key, value);
}
}
if let Some(merged) = codex_config_value {
cmd.env("CODEX_CONFIG", merged);
}

// Buzz-managed agents must never execute the operator's personal cron
// schedule. A goose ACP child starts a scheduler over the shared
// `schedule.json`, so a pool of N children fires every scheduled job N
// times — under the wrong identity and racing standalone goose.
//
// Set last, and with no operator-wins escape hatch, so it beats both a
// conflicting persona `extra_env` entry and any inherited parent value.
// Agent builds that don't recognize the variable ignore it.
cmd.env(GOOSE_SCHEDULER_DISABLED_ENV, "true");

// Spawn the agent in its own process group so SIGKILL doesn't propagate
// to the harness's own process group on Unix.
// tokio::process::Command::process_group is a stable tokio API (no extra imports needed).
Expand Down Expand Up @@ -1838,6 +1834,11 @@ impl AcpClient {
session_id = %notif.session_id,
input = payload.accumulated_input_tokens,
output = payload.accumulated_output_tokens,
// A subset of `input`, logged so downstream accounting can
// price it at the provider's cached rate. Always emitted,
// including as 0, so a parser can tell "no cache hits"
// apart from "this build predates the field".
cached = payload.accumulated_cached_input_tokens,
"goose usage update"
);
self.goose_usage.record(&notif.session_id, payload);
Expand Down Expand Up @@ -2851,74 +2852,75 @@ mod tests {
.expect("failed to spawn test script")
}

/// Spawn a script that echoes the named env vars as the child observes
/// them, one per line. `<unset>` means the child did not receive the var.
async fn spawn_and_read_child_env(
vars: &[&str],
/// Spawn a probe script whose file name carries a runtime identity (e.g.
/// `hermes-acp`) and return the value of `var` as the child observed it.
/// `<unset>` means the child did not receive the var.
#[cfg(unix)]
async fn spawn_named_and_read_child_env(
file_name: &str,
var: &str,
extra_env: &[(String, String)],
) -> Vec<String> {
let script = vars
.iter()
.map(|var| format!("printf '%s\\n' \"${{{var}:-<unset>}}\""))
.collect::<Vec<_>>()
.join("\n");
let mut client = AcpClient::spawn("bash", &["-c".into(), script], extra_env, false)
) -> String {
use std::os::unix::fs::PermissionsExt;

let dir = std::env::temp_dir().join(format!("buzz-acp-env-probe-{}", uuid::Uuid::new_v4()));
std::fs::create_dir_all(&dir).expect("create env probe dir");
let path = dir.join(file_name);
std::fs::write(
&path,
format!("#!/bin/sh\nprintf '%s\\n' \"${{{var}:-<unset>}}\"\n"),
)
.expect("write env probe script");
let mut permissions = std::fs::metadata(&path).expect("stat probe").permissions();
permissions.set_mode(0o700);
std::fs::set_permissions(&path, permissions).expect("chmod probe");

let mut client = AcpClient::spawn(
path.to_str().expect("probe path is UTF-8"),
&[],
extra_env,
false,
)
.await
.expect("spawn env probe script");
let observed = client
.reader
.next()
.await
.expect("failed to spawn env probe script");
let mut observed = Vec::with_capacity(vars.len());
for var in vars {
observed.push(
client
.reader
.next()
.await
.unwrap_or_else(|| panic!("child produced no output for {var}"))
.expect("child stdout was not readable"),
);
}
.unwrap_or_else(|| panic!("child produced no output for {var}"))
.expect("child stdout was not readable");
client.shutdown().await;
std::fs::remove_dir_all(&dir).expect("remove env probe dir");
observed
}

/// Every spawned agent must be told not to run the operator's cron
/// schedule, without the caller having to opt in.
/// Buzz-owned Hermes processes get the configured-MCP isolation default,
/// and an explicit persona entry still overrides it (defaults are applied
/// before `extra_env`, so the later `Command::env` write wins).
#[cfg(unix)]
#[tokio::test]
async fn spawn_injects_scheduler_disabled_env_by_default() {
let observed = spawn_and_read_child_env(&[GOOSE_SCHEDULER_DISABLED_ENV], &[]).await;
async fn spawn_applies_runtime_env_defaults_with_extra_env_precedence() {
const VAR: &str = "HERMES_ACP_SKIP_CONFIGURED_MCP";
if std::env::var_os(VAR).is_some() {
// Inherited parent values win over both layers; the default and
// override behavior below is unobservable in such an environment.
return;
}

assert_eq!(
observed,
vec!["true"],
"{GOOSE_SCHEDULER_DISABLED_ENV} must be injected into every spawn"
spawn_named_and_read_child_env("hermes-acp", VAR, &[]).await,
"1",
"Hermes spawns must default {VAR}=1"
);
assert_eq!(
spawn_named_and_read_child_env("hermes-acp", VAR, &[(VAR.into(), "0".into())]).await,
"0",
"an explicit extra_env entry must override the runtime default"
);
}

/// Persona config must not be able to re-enable the scheduler: this is a
/// correctness invariant, not an operator-tunable default, so the
/// injection is set after (and therefore wins over) the `extra_env` loop.
///
/// The control var pins that `extra_env` really did reach the child, so a
/// pass here means the conflicting entry lost the fight rather than
/// `extra_env` being dropped wholesale.
#[tokio::test]
async fn spawn_scheduler_disabled_env_overrides_conflicting_extra_env() {
let extra_env = vec![
(
GOOSE_SCHEDULER_DISABLED_ENV.to_string(),
"false".to_string(),
),
(
"BUZZ_ENV_PROBE_CONTROL".to_string(),
"delivered".to_string(),
),
];
let observed = spawn_and_read_child_env(
&[GOOSE_SCHEDULER_DISABLED_ENV, "BUZZ_ENV_PROBE_CONTROL"],
&extra_env,
)
.await;
assert_eq!(
observed,
vec!["true", "delivered"],
"a persona extra_env entry must not override {GOOSE_SCHEDULER_DISABLED_ENV}"
spawn_named_and_read_child_env("other-agent", VAR, &[]).await,
"<unset>",
"non-Hermes spawns must not receive Hermes defaults"
);
}

Expand Down
2 changes: 2 additions & 0 deletions crates/buzz-acp/src/base_prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ For explicit changes to an existing personal agent, use `buzz agents draft-updat

- Use the person's **exact full display name** after `@` (e.g., `@Will Pfleger`, not `@Will`). Partial names fail silently.
- Do NOT format mentions with bold, italic, or backticks — it breaks notification delivery.
- When you know intended recipient pubkeys, send readable `@Name` text and pass the identities separately in the same command: `buzz messages send ... --content "@Name ..." --mention <hex-or-npub>`. Repeat `--mention` for multiple recipients. Any explicit identity (`--mention` or `nostr:npub...`) permits unresolved or ambiguous `@Name` text as presentation-only; uniquely resolved member names still add their own recipients. Include a pubkey for every presentation-only name that should notify. The success JSON's `mention_pubkeys` comes from the signed event and is the delivery evidence; no follow-up verification command is needed.
- Without `--mention`, the CLI resolves `@Name` against current channel members. It stops before sending on an unresolved/ambiguous name or a mentioned pubkey that is not a member. For a non-member, add them explicitly with `buzz channels add-member` only when authorized, then retry. Sending never changes membership automatically.
- Only `@mention` when you need their attention. Don't mention in narrative (e.g., "coordinating with Duncan" — no `@`). Naming someone while talking *about* them is narrative — "waiting on @morgan", "until @morgan brings work", "I'll loop in @morgan later". Drop the `@`. Every mention sends a notification; a mention nobody needs to act on is a false alarm.

### Callback Mentions
Expand Down
Loading
Loading