Skip to content

feat(mesh): upgrade embedded mesh to v0.74 and harden shared compute (split 1/2 of #3467) - #3741

Merged
tlongwell-block merged 3 commits into
mainfrom
eva/mesh-0-74-split
Jul 30, 2026
Merged

feat(mesh): upgrade embedded mesh to v0.74 and harden shared compute (split 1/2 of #3467)#3741
tlongwell-block merged 3 commits into
mainfrom
eva/mesh-0-74-split

Conversation

@tlongwell-block

Copy link
Copy Markdown
Collaborator

Summary

This is part 1 of 2 split out from #3467 (per Tyler's request), carrying only the mesh-scoped changes. The agent/ACP response-behavior changes and the new send_message tool stay in #3467 as part 2. All commits are @michaelneale's work, cherry-picked with authorship preserved.

  • Upgrade embedded Mesh to v0.74.0 (tag-pinned instead of commit rev) and use canonical Gemma model IDs.
  • Keep shared compute serving through member joins, roster changes, app recovery, and community switching.
  • Wait for actual model readiness and avoid resuming incomplete downloads after quit.
  • Leave BUZZ_AGENT_THINKING_EFFORT unset by default so each model's chat template picks its own thinking default (none suppressed Gemma tool-calling entirely; pinning low made Qwen3 burn ~4x output budget). Explicit agent/persona/global values still win.

Relationship to #3467

Contains the mesh commits from #3467 (2cd640b23, 0ad81c341, ad13ed841) rebased onto current main, with one deliberate exclusion: the crates/buzz-agent/src/llm.rs reasoning→text parser change from 2cd640b23 is not here. That change unconditionally affects every OpenAI-compat/Responses provider, so it belongs with the reply-behavior work in part 2, where it can be reviewed as what it is.

Not included (remaining in #3467 / part 2):

  • typed send_message tool in dev-mcp + BUZZ_ACP_SEND_MESSAGE_TOOL gating
  • plain-reply delivery fallback in buzz-acp (BUZZ_ACP_DELIVER_PLAIN_REPLIES)
  • the mesh_agent_e2e P5/P6 rewrite (exists to prove the reply path)
  • the two env.insert preset opt-ins in relay_mesh.rs for the flags above
  • the llm.rs parser change

This PR is independently mergeable; part 2's flags are all off by default so it can land before or after.

Testing

  • cargo test -p buzz-relay --locked — 780 passed (one telemetry test is order-sensitive under parallel default settings; passes in the pre-push suite and standalone, unrelated to this diff — files untouched here).
  • just desktop-tauri-test (default features) — 1877 passed.
  • cargo test --locked --features mesh-llm in desktop/src-tauri — 1961 passed, including the new relay-mesh preset and coordinator/recovery tests.
  • Both Cargo.locks resolve with --locked against the v0.74.0 tag.
  • Full pre-push hook suite green (rust-tests, desktop-check/test, tauri checks).

Live validation of the mesh v0.74 upgrade itself is documented on #3467 (two-Mac cross-version test).

michaelneale and others added 3 commits July 30, 2026 09:32
Pin mesh-llm to the v0.74.0 tag instead of a commit rev, and carry the
desktop-side changes that upgrade needs: catalog/coordinator/recovery
updates, the relay-mesh agent preset, and restart behaviour that survives
community boundary changes and roster churn.

(Split from #3467: the buzz-agent llm.rs parser change in the original
commit is deliberately excluded here — it modifies response behaviour for
every OpenAI-compat provider and belongs with the agent/ACP reply-behaviour
changes, not the mesh upgrade.)

Signed-off-by: Michael Neale <michael.neale@gmail.com>
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
The relay-mesh preset defaulted BUZZ_AGENT_THINKING_EFFORT to "none" to
spend the output budget on an answer instead of hidden reasoning. On local
models that also switches tool calling off: llama.cpp's Gemma grammar is
lazy under `tool_choice: auto`, and `reasoning_effort: "none"` keeps the
model on its prose completion path so it never emits the tool-call token.
The agent then answers in plain content, which is never published, and the
turn reports success with nothing in the channel.

Isolated by replaying a captured production request against a local mesh
node and varying one field: with the real prompt and toolset,
gemma-4-E4B emitted a tool call 0/8 times at "none", 3/4 at "minimal",
and 4/4 at "low" (4/4 with the field omitted entirely). Default to "low",
which buys enough deliberation to select a tool without spending the
budget on hidden reasoning. Still a default, so explicit
agent/persona/global values are preserved.

The mesh e2e harness claims to supply exactly what apply_relay_mesh_env()
does, so update it in lockstep — pinning "none" there would test a config
the product no longer ships and hide the regression P5/P6 exist to catch.

Signed-off-by: Michael Neale <michael.neale@gmail.com>
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Shared compute pinned BUZZ_AGENT_THINKING_EFFORT. That is not a sampling
hint: mesh translates `reasoning_effort` into the chat template's
`enable_thinking` flag, so any value Buzz sends overrides the default the
model authors shipped — and the right value is model-specific.

Measured against a request captured from the real agent (full ~43k-token
system prompt, real dev-mcp toolset, tool_choice=auto):

  effort   gemma-4-E4B   Qwen3-8B   Qwen3-8B output tokens
  none         0/8          8/8               ~121
  low          4/4          7/8               ~470
  unset        6/6          8/8               ~500

`none` (the previous default, chosen when shared compute was Qwen3-only)
stops gemma tool-calling outright, so its replies were never published.
Enabling thinking instead makes Qwen3 spend ~4x the output budget, which
risks the 4096 cap this preset sets.

Omitting the field leaves `enable_thinking` as None, so mesh passes no
override and each model uses its own template default: 3/3 delivery on
gemma-4-E4B, gemma-4-26B-A4B, gemma-4-31B and Qwen3-8B, and 3/3 replies
through the real buzz-acp + buzz-agent path against a live relay.

Thinking levels are unaffected: an explicit agent/persona/global value is
still preserved and applied.

Signed-off-by: Michael Neale <michael.neale@gmail.com>
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
@tlongwell-block
tlongwell-block requested a review from a team as a code owner July 30, 2026 13:58
@tlongwell-block
tlongwell-block merged commit 4933672 into main Jul 30, 2026
32 checks passed
@tlongwell-block
tlongwell-block deleted the eva/mesh-0-74-split branch July 30, 2026 15:37
wpfleger96 added a commit that referenced this pull request Jul 30, 2026
…chive

* origin/main: (22 commits)
  feat(catalog): resolve publisher display name in catalog detail pane (#3640)
  feat(mesh): upgrade embedded mesh to v0.74 and harden shared compute (split 1/2 of #3467) (#3741)
  docs(nips): specify kind:30621 multi-repo projects (NIP-MP) (#3163)
  Refine agent sharing dialog (#3699)
  desktop: enable getUserMedia in the Linux WebKitGTK webview (#3607)
  fix: align responsive agent views (#3688)
  Add macOS agent menu-bar menu (#3565)
  Fix pending message feedback (#3543)
  fix(desktop): remove remaining Projects panel fills (#3742)
  feat(mobile): desktop-parity emoji and thread experience (#3485)
  desktop: restore direct community member adds (#3634)
  fix(desktop): explain open agent access (#2561)
  fix(cli): resolve agents from owner records (#3178)
  fix(desktop): remove Projects overview card fills (#3416)
  feat(replica): portable heartbeat-token fence with snapshot-local reader routing (#3268)
  fix(git): channel binding tooling + author remediation for unbound repos (#3626)
  feat: configure S3 URL addressing style (#3400)
  feat: add first-class OpenRouter provider support (#1975)
  feat(agent,acp): wire provider total_tokens through NIP-AM publish chain (#3593)
  chore(release): release Buzz Desktop version 0.5.2 (#3624)
  ...

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wpfleger96 pushed a commit that referenced this pull request Jul 30, 2026
…g-pipeline

* origin/main:
  feat(catalog): resolve publisher display name in catalog detail pane (#3640)
  feat(mesh): upgrade embedded mesh to v0.74 and harden shared compute (split 1/2 of #3467) (#3741)
  docs(nips): specify kind:30621 multi-repo projects (NIP-MP) (#3163)

Signed-off-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
joahg added a commit to joahg/buzz-dev-mode that referenced this pull request Jul 30, 2026
…-style

* origin/main:
  fix(acp): preserve truncated thread context (block#3340)
  feat(catalog): resolve publisher display name in catalog detail pane (block#3640)
  feat(mesh): upgrade embedded mesh to v0.74 and harden shared compute (split 1/2 of block#3467) (block#3741)
  docs(nips): specify kind:30621 multi-repo projects (NIP-MP) (block#3163)
  Refine agent sharing dialog (block#3699)
  desktop: enable getUserMedia in the Linux WebKitGTK webview (block#3607)
  fix: align responsive agent views (block#3688)
  Add macOS agent menu-bar menu (block#3565)
  Fix pending message feedback (block#3543)
  fix(desktop): remove remaining Projects panel fills (block#3742)
  feat(mobile): desktop-parity emoji and thread experience (block#3485)
  desktop: restore direct community member adds (block#3634)
  fix(desktop): explain open agent access (block#2561)
  fix(cli): resolve agents from owner records (block#3178)
  fix(desktop): remove Projects overview card fills (block#3416)
  feat(replica): portable heartbeat-token fence with snapshot-local reader routing (block#3268)

Signed-off-by: Joah Gerstenberg <joah@squareup.com>
wpfleger96 added a commit that referenced this pull request Jul 30, 2026
* origin/main: (59 commits)
  Fix video reviews in thread replies (#3719)
  feat(release): make desktop releases immutable (#3568)
  Make relay reconnect backoff authoritative (#3774)
  feat(desktop): add password-protected backups in settings (#3701)
  fix(desktop): reuse profiles when joining communities (#2155)
  Render mobile agent mention chips (#3702)
  fix(catalog): update Amp description (#3758)
  fix(acp): preserve truncated thread context (#3340)
  feat(catalog): resolve publisher display name in catalog detail pane (#3640)
  feat(mesh): upgrade embedded mesh to v0.74 and harden shared compute (split 1/2 of #3467) (#3741)
  docs(nips): specify kind:30621 multi-repo projects (NIP-MP) (#3163)
  Refine agent sharing dialog (#3699)
  desktop: enable getUserMedia in the Linux WebKitGTK webview (#3607)
  fix: align responsive agent views (#3688)
  Add macOS agent menu-bar menu (#3565)
  Fix pending message feedback (#3543)
  fix(desktop): remove remaining Projects panel fills (#3742)
  feat(mobile): desktop-parity emoji and thread experience (#3485)
  desktop: restore direct community member adds (#3634)
  fix(desktop): explain open agent access (#2561)
  ...

# Conflicts:
#	desktop/scripts/check-file-sizes.mjs
wpfleger96 pushed a commit that referenced this pull request Jul 30, 2026
* origin/main: (29 commits)
  feat(desktop): raise the install ceiling and make installs observable (#3368)
  fix(db): isolate usage metrics advisory-lock test on scratch DB (#3670)
  Add Devin as a preset ACP harness (#3225)
  feat(desktop): improve agent activity header ui (#3321)
  perf(presence): reduce heartbeat frequency (#3783)
  Tighten continuation message rows (#3724)
  Fix video reviews in thread replies (#3719)
  feat(release): make desktop releases immutable (#3568)
  Make relay reconnect backoff authoritative (#3774)
  feat(desktop): add password-protected backups in settings (#3701)
  fix(desktop): reuse profiles when joining communities (#2155)
  Render mobile agent mention chips (#3702)
  fix(catalog): update Amp description (#3758)
  fix(acp): preserve truncated thread context (#3340)
  feat(catalog): resolve publisher display name in catalog detail pane (#3640)
  feat(mesh): upgrade embedded mesh to v0.74 and harden shared compute (split 1/2 of #3467) (#3741)
  docs(nips): specify kind:30621 multi-repo projects (NIP-MP) (#3163)
  Refine agent sharing dialog (#3699)
  desktop: enable getUserMedia in the Linux WebKitGTK webview (#3607)
  fix: align responsive agent views (#3688)
  ...

Signed-off-by: npub1g8493u0xfsjrvflg4n08ezd7vec99mnwzlv0qgwpr9d7gvjwhuzqx59rhw <41ea58f1e64c243627e8acde7c89be667052ee6e17d8f021c1195be4324ebf04@buzz.block.builderlab.xyz>
tlongwell-block added a commit that referenced this pull request Jul 31, 2026
…3763)

## Why

A Buzz agent's assistant text and reasoning are never shown to anyone —
only what it posts through the CLI. A turn that runs fifteen tool calls
and never publishes is a silent failure: the requester waits on a result
that was produced and thrown away.

This adds an optional reminder at the end-of-turn gate, off by default.

Tyler asked for it in buzz-mesh; plan iterated to **9.5/10 with @wren**
(Minimalness 9.7, Elegance 9.5, Correctness 9.3).

## What

`BUZZ_AGENT_REQUIRE_REPLY=1` (default off, per-agent opt-in). A turn
about to end with no recognized attempt to post gets a reminder and is
rerolled. **At most two, then the turn ends regardless** — the guard
catches accidental omission, it does not compel speech. The reminder
text explicitly licenses silence so it cannot fight the base prompt's
"silence is usually correct."

**This is not a new MCP hook.** `RunCtx::run` *is* the turn, so the two
per-turn locals need no plumbing, and every tool call already passes
through it with arguments visible. The objection is appended at the
existing `_Stop` gate and rides `push_hook_outputs_as_tool_results`, so
the model receives it as a lower-trust tool result with `{hook, server,
text}` attribution. No new trust path, no new lifecycle event, no
dev-mcp or CLI protocol change.

Earlier revisions of this plan needed four crates (a `_UserPromptSubmit`
hook, a marker file, a `buzz-cli` change, dev-mcp state). Tyler pointed
out the agent already knows both facts; that deleted all of it. Net
runtime change is ~35 lines in `agent.rs` + ~4 in `config.rs`.

### Recognition contract

A registered non-hook tool whose qualified name ends in `__shell`, whose
`command` argument contains `messages send` or `reactions add`.

- **The `__` separator is exact, not approximate.** Given `has()` +
`!is_hook()`, `ends_with("__shell")` is *provably equivalent* to a bare
name of `shell`: registration forbids `__` in server and bare names
(`mcp.rs:227,268`) and qnames are `{server}__{bare}`, so a trailing
`__shell` could only straddle the separator if the bare name began with
`_` — which `is_hook` excludes. Without the separator, `powershell` and
`noshell` would match.
- **Reads the structured `command` field**, not serialized arguments, so
a `description` that quotes a send cannot disarm the guard, and a
non-string `command` is rejected rather than coerced.
- **Detects an attempt, not a successful publish.** A failed send
already returns non-zero exit and error JSON — louder than this
reminder. The variable is named `buzz_reply_call_seen` so the code can't
pretend otherwise.
- **Checked after the per-turn tool-call cap**, since a discarded call
never ran.
- `messages send` also covers `messages send-diff`. Reactions count
because the base prompt directs agents to react rather than post a bare
acknowledgement.

**Known limits, both deliberate and documented:** a command assembled at
runtime (`$CMD`) or hidden in a wrapper script is missed; text that
merely quotes a send (`echo "buzz messages send"`) matches. Missing a
real post is the expensive direction and substring matching is the
forgiving one there. Neither edge is pinned by a test, so the matcher
stays free to improve.

### Budget

Reminders share `BUZZ_AGENT_STOP_MAX_REJECTIONS`, the existing outer cap
on every end-turn objection. Default 3 fits both; at 1 only one fits; at
0 the guard is off with the hooks. A round carrying both a hook
objection and a reminder costs one rejection and delivers both texts. An
independent budget would either violate that bound or need a second
arbitration rule.

## Prior art

- **#3467** (closed) built the same detector one layer up in `buzz-acp`
for a different remedy. None of its symbols are on main — this borrows
its permission to be coarse, but reads structured data that ACP didn't
have.
- **#3648** (open) detects turns with *no output at all*; a turn with
fifteen tool calls and no post counts as output there, so it does not
cover this case.
- **#3741** (merged) is mesh-only.

## Testing

**14 new tests.** 4 unit tests on the matcher; 10 integration tests
through the ACP wire harness: off by default, `=0` still off, opted-in
silent → exactly 2 reminders then `end_turn`, registered `fake__shell`
send → 0 reminders, hallucinated `fake__shell` → still reminded, publish
call truncated past the 64-call cap → still reminded, budget 1 → 1
reminder, budget 0 → off, combined `_Stop` hook objection + reminder →
one round both texts and after 2 reminders the hook objection continues
alone, unparseable `=true` → startup error naming the key.

**10 mutation checks, each breaking a specific named test** — neutralize
the nag cap, stop sharing the budget, neutralize `buzz_reply_call_seen`,
drop `has`/`is_hook`, ignore the flag, drop the `__`, drop `reactions
add`, read serialized args, move detection before truncation.

`tests/bin/fake_mcp.rs` gains `FAKE_MCP_SHELL_TOOL=1`: it previously
exposed no tool with a bare name of `shell`, so the satisfied-guard path
was untestable.

Full `cargo test -p buzz-agent` green at 9e0ae1f; clippy `-D warnings`
and `cargo fmt --check` clean.

**Unrelated flake found:**
`cancelled_turn_with_usage_emits_notification_before_response`
(`tests/fake_llm.rs`) is timing-sensitive. Under 10 loaded cores it
fails **2/20 on this branch and 1/20 at unmodified
`origin/main@02be413`** — pre-existing, not caused by this change
(which is inert without the env var). Flagging so it isn't misattributed
to the next PR that's open when CI hits it.

## Docs

`crates/buzz-agent/README.md` is the primary home (env var, recognition
contract, limits, budget interaction). `docs/MCP_DRIVEN_HOOKS.md` gets a
short cross-reference explaining this is *not* a hook — otherwise
readers hunt for a `_ReplyGuard` tool that doesn't exist.

---------

Signed-off-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com>
Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@buzz.block.builderlab.xyz>
Co-authored-by: Dawn (sprout agent) <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@buzz.block.builderlab.xyz>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants