Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
2527efa
feat(buzz-agent-core): drive buzz-agent's ACP surface on the goose li…
michaelneale Jul 27, 2026
63efbb2
feat(buzz-agent-core): pin goose by git rev, wire session/set_model
michaelneale Jul 27, 2026
4612452
fix(buzz-agent-core): advertise the model catalog on session/new
michaelneale Jul 27, 2026
e9aca45
feat(buzz-agent-core): implement the _Stop veto and _PostCompact re-i…
michaelneale Jul 27, 2026
22f5e70
feat(buzz-agent-core): inject [Reflect] on failed tool results
michaelneale Jul 27, 2026
737bde4
test(buzz-agent-core): validate against the real buzz-dev-mcp
michaelneale Jul 27, 2026
b631552
fix(buzz-agent-core): drain on cancel instead of dropping the stream
michaelneale Jul 27, 2026
fbe120f
test(buzz-agent-core): cover session/steer, the last untested ACP method
michaelneale Jul 27, 2026
3596e8e
fix(buzz-agent-core): correct a false claim about GooseMode::default()
michaelneale Jul 27, 2026
c12853e
build(buzz-agent-core): emit the binary as `buzz-agent`
michaelneale Jul 27, 2026
1bd2d94
docs(buzz-agent-core): add HANDTEST.md and a `just agent-core` recipe
michaelneale Jul 27, 2026
1f6b75a
refactor(buzz-agent): swap the agent loop onto the goose library
michaelneale Jul 27, 2026
ac6aed1
docs(buzz-agent): rewrite HANDTEST.md for the swap
michaelneale Jul 27, 2026
81eec0f
fix(buzz-agent): restore `auth` subcommand and close config compat gaps
michaelneale Jul 27, 2026
43d3698
feat(buzz-agent): restore relay-mesh adaptive MoA routing
michaelneale Jul 27, 2026
55696fc
docs(buzz-agent): pin the one mesh behaviour that is NOT parity
michaelneale Jul 28, 2026
41974bc
fix(buzz-agent): catch mesh-llm's 502 MoA failure path, not just the 503
michaelneale Jul 28, 2026
6c14563
Merge remote-tracking branch 'origin/main' into micn/buzz-agent-goose…
michaelneale Jul 28, 2026
fa8166e
fix(buzz-agent): address expert review — shutdown, cap race, usage model
michaelneale Jul 28, 2026
cca05c0
feat(buzz-agent): restore AGENTS.md hints and load_skill on the goose…
michaelneale Jul 28, 2026
66d1bff
chore(deny): allow MIT-0 for goose's borrow-or-share transitive dep
michaelneale Jul 28, 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
2,697 changes: 2,426 additions & 271 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ members = [
"crates/buzz-audit",
"crates/buzz-acp",
"crates/buzz-agent",
"crates/buzz-model-catalog",
"crates/sprig",
"crates/buzz-test-client",
"crates/buzz-ws-client",
Expand Down
16 changes: 16 additions & 0 deletions crates/buzz-agent/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,22 @@ name = "fake-mcp"
path = "tests/bin/fake_mcp.rs"

[dependencies]
# For the `buzz-agent auth <provider>` subcommand only. Provider auth for the
# agent loop itself is goose's; this is the interactive PKCE login that seeds
# the token cache that goose (and the desktop model picker) then read.
buzz-model-catalog = { path = "../buzz-model-catalog" }
goose = { git = "https://github.com/aaif-goose/goose", rev = "305849b71709b95b86ed9f11bd3bc939899c0aab", default-features = false, features = ["rustls-tls"] }
goose-provider-types = { git = "https://github.com/aaif-goose/goose", rev = "305849b71709b95b86ed9f11bd3bc939899c0aab" }
# Resolution guard: goose pins icu_locale "=2.1.1" (needs icu_collections
# ~2.1.1) while url 2.5.x -> idna -> idna_adapter 1.2.2 pulls icu_normalizer
# 2.2.0 (needs icu_collections ~2.2.0). Only one 2.x icu_collections can be
# selected. 1.2.0 is the last idna_adapter on ICU4X 1.x, which keeps IDNA off
# the icu_collections 2.x line entirely and lets both resolve.
idna_adapter = "=1.2.0"
tokio-util = { version = "0.7", features = ["rt"] }
futures = "0.3"
anyhow = "1"

tokio = { workspace = true, features = ["rt-multi-thread", "macros", "io-std", "io-util", "sync", "process", "time", "net"] }
serde = { workspace = true }
serde_json = { workspace = true }
Expand Down
96 changes: 96 additions & 0 deletions crates/buzz-agent/HANDTEST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Hand-testing `buzz-agent`

This is a **swap**, not a new thing. `buzz-agent` keeps its name, its binary,
its ACP contract and its place in the workspace — only the internals changed
from a hand-written agent loop to the `goose` crate used as a Rust library.

So there is nothing special to run. The normal flow *is* the test:

```bash
just dev # the whole app
# or
just relay # terminal 1
just goose # terminal 2 — agent against that relay
```

Both already build and use the swapped `buzz-agent`. If you see a difference,
that is the bug.

## What to look at

Ordered by risk. The automated suite (41 tests) covers each of these at the
stdio layer; this list is the part only a human can judge.

### 1. Persona actually arrives

```
@fizz who are you?
```

Should answer *as Fizz* and know the `buzz` CLI exists. A generic goose answer
means the system prompt was dropped — the exact failure that makes plain-ACP
embedding impossible, and the reason this uses the library API instead.

### 2. `_Stop` veto

```
@fizz make a todo list with 3 items, then stop immediately without doing them
```

Must refuse to stop while items are open. Capped at 3 vetoes, so it ends
eventually regardless. Look for `_Stop hook vetoed end of turn` in the log.

### 3. Streaming feel

The old loop emitted one chunk per round; goose streams token-by-token. Tests
prove the relay is not write-amplified (chunks are coalesced by identity key,
flushed at 500ms, paced at 167ms/90-per-minute), but **only a human can say
whether it feels better or worse in the desktop app.** This is the most likely
source of "something is off".

### 4. Cancel mid-tool

Ask for something long (`count slowly to 100 with a shell sleep`), then stop.
The turn should end promptly with **no tool call left spinning**. Cancellation
is a cooperative drain with a 5s budget — dropping the stream instead would
leave the MCP child running and the spinner stuck forever. That bug existed and
is fixed; this is the visual confirmation.

### 5. Steering

Send a second message while the agent is working. It should be absorbed into
the running turn, not cancel-and-restart it.

### 6. Model picker

Should list models, not just the current one. An absent catalog is degraded UX,
never a session failure.

### 7. Tool hygiene — known deviation

The model can now *see* `_Stop` and `_PostCompact`: goose's allowlist gates
advertising and dispatch through the same cache, so hiding them would also make
them undispatchable and break the veto. A system-prompt extension tells the
model to leave them alone. Watch for it calling `_Stop` itself — if that
happens the guidance needs strengthening.

## Comparing against the old behaviour

The old loop is gone, so A/B means checking out `main` in a second worktree and
running that relay side by side.

## Known gaps

* **Never run against a real provider.** All automated coverage uses a fake SSE
server. Databricks *chat* now goes through goose entirely and is unexercised
— though Databricks *model discovery* still uses our own code, moved to
`buzz-model-catalog` (the desktop cannot link goose: native `sqlite3`
collision with its own rusqlite).
* **Relay-mesh MoA is restored but only tested against a fake router.** With
`mesh-llm` running, a relay-mesh agent on `auto` should switch to the virtual
`mesh` model within ~2 turns once ≥2 models are live, and fall back to `auto`
within 30s of the mesh shrinking. Grep the agent log for
`relay-mesh auto:`.
* Binary is roughly +22.7 MiB raw / +6.0 MiB gzip.
* Nothing changed in packaging or the harness catalog — same sidecar name, so
nothing needed to.
Loading
Loading