Repository navigation
Description
Summary
We run Buzz Desktop agents on goose ACP against a custom OpenAI-compatible provider and self-hosted models (not stock cloud providers). In that configuration we hit a multi-layer failure mode that looks like “agent is online but never replies in channel,” even when goose completes the turn successfully.
This is an edge case relative to default Buzz/goose setups that use first-party providers and models that reliably emit tool calls. It is a real class for internal proxies and self-hosted OpenAI-compatible endpoints. We are filing before related PRs so the product gap is documented for maintainers.
Environment (reproducible class)
| Piece | Our setup |
|---|---|
| Host | Buzz Dev Desktop (tauri dev / managed agents) |
| Runtime | goose acp (goose 1.44.0 measured) via buzz-acp harness |
| Provider | Custom OpenAI-compatible endpoint (example: custom_actvite → self-hosted OpenAI API) |
| Models | e.g. deepseek-class and qwen-coder-class on our infra |
| Agent access | Often respond_to=owner-only |
| Publish path | Channel visibility requires a successful buzz messages send (tool or harness content-delivery fallback) |
CLI goose against the same custom provider/models can work from an interactive login shell while Desktop-spawned ACP children fail or “succeed silently.”
Symptoms
- Agent harness is up (presence online, pool ready, subscribed to channel).
- Owner
@mentionproduces typing/activity (or nothing visible). - No kind:9 channel reply (thread or top-level).
- Goose session may still show a full assistant turn (text /
writetools) in session DB. - Harness logs may show either:
agent_returned/ JSON-RPC -32603 Internal error with goose dataProvider not set
(verbatim class:Error getting agent reply: Provider not set), or- turn completion without any publish tool and without harness content-delivery fallback (pre-S1 / clobbered binaries).
At default info logging, successful turns often leave no created session lines in buzz_acp logs — “log frozen after membership” is not a falsifier for “turn never ran.” Watch for new -32603 or channel kind:9 instead.
Expected
- Desktop-spawned goose ACP resolves custom provider secrets the same way CLI goose does (or fails loudly in UI).
- When a channel/DM mention turn ends with user-visible assistant text and no successful publish tool call, the harness still delivers exactly one channel/DM reply (model-agnostic).
- Managed-agent harness binary choice is stable across Desktop restarts (not clobbered by cargo
target/debugrebuilds in dev).
Actual
A. Custom provider secrets on Desktop spawn (goose + Desktop env)
Desktop-spawned goose acp children inherit a thin environment. With GOOSE_PROVIDER=<custom> set but without the host’s keyring/secrets path:
GOOSE_DISABLE_KEYRING=1is the required switch so goose can load~/.config/goose/secrets.yamlwhen the system keyring is unavailable under GUI/Desktop spawn.- The provider’s expected API key env (class:
CUSTOM_*_API_KEY; our measured name:CUSTOM_ACTVITE_API_KEY, often mapped from a shell-only name likeGOOSE_ACTVITE_API_KEYin login bashrc) is a useful belt, not always mandatory if secrets.yaml is complete and keyring is disabled.
Without that setup, goose ACP fails with -32603 / Provider not set. Interactive CLI works because the login shell already sets keyring-disable (and often key mapping).
Measured falsifier (goose 1.44.0): secrets.yaml only + no GOOSE_DISABLE_KEYRING → Provider not set class; GOOSE_DISABLE_KEYRING=1 alone → stopReason: end_turn OK.
Workaround we applied (ops, not product): persist those env vars on the managed agent (and/or global agent config) and respawn the agent process.
B. Self-hosted models that complete without tool calls (Buzz publish contract)
Buzz channel UX only shows kind:9. Success path historically depended on the model invoking a publish tool (e.g. goose-namespaced buzz_publish__buzz_messages_send / shelling buzz messages send).
With a self-hosted deepseek-class model + toolshim already enabled (GOOSE_TOOLSHIM=true):
- Tool path sometimes works (MCP is fine when called).
- On short creative turns the model often returns assistant text only (
toolRequestcount = 0) despite VISIBILITY / “must publish” Context in the user message. - A sibling model (qwen-coder-class) more often called the publish tool under the same contract.
Result: goose “succeeds,” channel stays silent. Prompt-forced wording is not an acceptable product fix.
Product direction that worked for us (S1): buzz-acp content-delivery fallback — on end of turn, if no successful publish tool fired, post undelivered assistant text once as a channel reply (log class: content-delivery fallback: posted undelivered agent content as channel reply). Model-agnostic; preserves PUBLISH ONCE / no double-post when the tool already succeeded.
C. Dev Desktop harness path clobber (aggravating)
With bare acp_command: "buzz-acp", Desktop workspace resolution (resolve_workspace_command in desktop/src-tauri/src/managed_agents/discovery.rs) prefers cargo workspace target/debug/buzz-acp over ~/.local/bin. Full Desktop / tauri dev restart rebuilds/relinks that path and can replace a known-good harness (with content-delivery fallback / VISIBILITY Context) with an older debug binary that lacks it. Agents look healthy; publish path regresses.
Measured example: pre-S1 SPAWN build had 0 content-delivery fallback strings; good user-installed S1 build had them. Live agents after full restart ran the pre-S1 binary until re-pinned.
Workaround we applied: pin affected agents’ acp_command to an absolute path outside cargo target (e.g. $HOME/.local/bin/buzz-acp) so the next cargo clobber cannot move those agents back onto pre-fallback binaries. Absolute paths skip workspace resolution when the path exists (command_looks_like_path).
Scope note: Layer C is primarily a dev / tauri cargo workspace clobber; packaged Desktop may pin differently.
Minimal reproduction sketch (custom provider edge case)
- Configure a Buzz managed agent: runtime goose ACP, custom OpenAI-compatible provider, self-hosted model (ideally one weak at tool calls, e.g. deepseek-class).
- Do not set
GOOSE_DISABLE_KEYRING=1(and omit provider API key env if secrets depend on keyring); start from Desktop (not login-shell goose). - Observe: turn error /
-32603Provider not set or fix secrets and continue. - With secrets fixed but without harness content-delivery fallback: owner
@Agent write a short sentence→ session has assistant text → zero channel kind:9. - With content-delivery fallback binary: same ordinary wording → exactly one kind:9.
What we believe should change in product
- Document Desktop goose + custom provider requirements (
GOOSE_DISABLE_KEYRING, provider key env names, secrets.yaml vs keyring under GUI). - Harden Desktop spawn so custom providers get the same secret resolution path as CLI (or fail loudly in UI with “provider not configured” instead of silent/emoji-only turns). Default-injecting
GOOSE_DISABLE_KEYRING=1for goose-runtime agents on hosts without a working system keyring would cover the common edge case. - Ship content-delivery fallback in
buzz-acpas the default for channel/DM mention turns so self-hosted / non-tool-calling models are not second-class for channel visibility. - Spawn resolution (dev): prefer absolute/user-installed harness, or stop resolving bare
buzz-acpexclusively to workspacetarget/debugin a way that silently reverts product fixes after every full Desktop rebuild. Absoluteacp_commandshould remain supported and recommended for local product binaries.
Out of scope / not claiming
- This is not “goose cannot use custom providers at all” — CLI and correctly-env’d ACP both work.
- This is not fixed by swapping every agent to a different model; we intentionally split models.
- Origin timeouts / CF 524 on a single self-hosted model route are capacity issues, distinct from Provider-not-set and publish-fallback classes.
- Goose steer / hung-turn classes (deadline extensions with no channel egress) are related operational noise but not required to reproduce A/B/C.
Acceptance criteria (for a fix PR)
- Custom provider agent started only from Desktop UI can complete a turn without manual shell keyring env (or UI blocks with a clear config error).
- Owner ordinary mention (no forced “you must publish” poetry) produces exactly one kind:9 when the model returns text and does not call publish tools.
- Same agent with tool-based publish still produces exactly one kind:9 (no double post).
- Full Desktop restart does not silently drop agents onto a harness build missing content-delivery fallback when an absolute pin or equivalent product default is set.
Suggested labels (if available)
bug, desktop, goose, acp, documentation
Activity
extraknown-alec commentedon Aug 2, 2026
I'm far less technically proficient than this original poster but I believe I'm having the same issue. Just spent all day trying to get an oMLX instance running local models (does so reliably the last 3 weeks) to return text to the Buzz discussion that initiated it.
Everything works great in Goose Desktop using either the built-in oMLX provider, or creating a custom OpenAI Compatible provider. However when setting up Buzz agents, the best outcome I can muster using either Goose+oMLX provider, or Goose+Custom Provider, is for the agent to (very slowly I'll add) request and reply but the turn never outputs to the chat window. I see it in the Activity view however.
As I said, it works well in Goose and much faster. No combination of settings, and aggressively wiping Buzz and reinstalling, or even creating a new community works.
iroiro147 commentedon Aug 2, 2026
Triage note from a fresh read — this is a really good write-up of three distinct failure modes (A=secrets flow, B=content-delivery fallback, C=workspace clobber). They're independent enough that each could land as its own small PR; combining them into one 4-acceptance-criteria PR invites the kind of review deadlock none of us want.
Suggested split:
resolve_workspace_command(desktop/src-tauri/src/managed_agents/discovery.rs) — add aboolargument or a config knob that, whenacp_commandis an absolute path that already exists on disk, skips the workspace-target resolution entirely. Acceptable behavior ontauri dev/packaged builds. ~5 LOC + tests.docs/agents/goose.mdcoveringGOOSE_DISABLE_KEYRING=1+ provider-specific env names for GUI spawns, and (optionally) warn in the agent add-wizard whenGOOSE_PROVIDER=custom_*is set withoutGOOSE_DISABLE_KEYRING. ~20 LOC docs + optional warn.I have capacity to ship C right now — it doesn't gate on A or B having a chosen direction. Opening a tiny PR for C so the obvious dev-workflow footgun is fixed while the longer conversations happen.
coldcanuk commentedon Aug 2, 2026
Status update (coldcanuk / actvite)
We opened and maintained a fork with the worktree + commits for Layer B (content-delivery fallback) and S1.1 once-publish-per-trigger (multi-step double/triple-post guard).
Fork / worktree / commits
/opt/repo/worktrees/wt-acp-s1-auto-publish-20260801fizz/acp-s1-auto-publishandfizz/acp-once-publish-stop(same tip)1d0708a9amainCommits on the branch (rebased onto current
block/buzzmain):bca8bab78—fix(acp): deliver plain-text replies when the model skips the send toolf68a1d7ae—fix(acp): confirm message delivery from tool outcome, not input textf5e5ecb89—feat(acp): S1 detect MCP buzz_messages_send tool names1d0708a9a—feat(acp): once-publish-per-trigger stop after first confirmed sendPull request (Layer B + S1.1)
→ #4191
Cites this issue. Scope is Layer B / B′ only (publish contract + once-publish stop). Layer A (custom-provider secrets /
GOOSE_DISABLE_KEYRING) remains out of that PR (docs/ops follow-up).Related work already in flight
target/debugclobber) — thank you @iroiro147 for the triage split and fix(desktop): prefer user-installed binaries over workspace target for bare commands #4284. That is complementary; our feat(acp): S1 content-delivery fallback + once-publish-per-trigger #4191 does not claim C.What this does not fix alone
GOOSE_DISABLE_KEYRING=1+ secrets) — Layer A.cargo build— Layer C / fix(desktop): prefer user-installed binaries over workspace target for bare commands #4284 + absoluteacp_commandpin as workaround.Happy to iterate on review feedback on #4191.
Pep939 commentedon Aug 4, 2026
Independent reproduction of this failure class on the first-party
buzz-agentruntime — not goose — which suggests the gap is in the harness publish path rather than in any particular third-party ACP implementation.Environment
tauri dev)wss://<name>.communities.buzz.xyz(also reproduced against a self-hosted relay)buzz-agentviabuzz-acpharnessopenai-compat→ Ollama on127.0.0.1:11434ornith:9b(local, 6.4 GB, fully GPU-resident)respond_todefault; owner @mention in an open channelThe controlled comparison
One message @-mentioning three agents at once, same channel, same instant:
claude-agent-acpcodex-acpbuzz-agent+ local OllamaBecause the three share one relay, one channel, one prompt and one moment, relay/channel/threading and the desktop's thread-render path are all controlled for — the two cloud runtimes' replies render normally in that same thread. The only variable is the runtime + local provider, matching this issue's class.
What is ruled out
Not the model returning empty text. Queried the same endpoint directly:
contentis populated and distinct fromreasoning, socrates/buzz-agent/src/agent.rs:212(response.text→agent_message_chunk) has real text to emit. The text does reach the desktop — it renders in the agent's observed session / activity pane. It just never becomes a channel event.Not inference failure. Ollama logs a clean turn every time, e.g.
[GIN] 200 | 9.701881956s | POST "/v1/chat/completions"with no errors, timed to the @mention.Not the thread-render bug in
docs/welcome-kickoff-silent-failures.md§4 — that hides replies that exist; here the sibling agents' replies render fine in the same open thread.Not
buzzPATH shadowing (buzz on PATH shadows the real headless CLI with the GUI-launch wrapper — silent no-op on messages send for ACP agents #3471).which buzzresolves to a real compiled CLI binary, not the GUI launcher wrapper.Harness signature
Agent log at
~/.local/share/xyz.block.buzz.app/agents/logs/<pubkey>__<hash>.log, immediately after the prompt:The assistant text was visible in the desktop at 05:06:45 — ~90 s before this shutdown. The same log shows 9
buzz-acp startinglines in a single day for this agent.I want to be explicit that I have not established whether that mid-turn shutdown causes the missing publish or is a downstream symptom — it is a signature, not a diagnosis. Flagging it because it lines up with this issue's "turn completion without any publish tool and without harness content-delivery fallback" bullet, and because a harness that tears down while
waiting for in-flight promptsis a plausible place for a reply to be dropped.Happy to run instrumented builds or pull further logs on this setup if useful — it reproduces on demand.
blabj commentedon Aug 16, 2026
Additional data point: same silent-failure class with custom OpenAI-compatible provider but with an opencode-override agent — root cause appears to be
mcp_cmdnever being attachedSetup: Buzz Desktop 0.5.14 (macOS), custom managed agent with
agent_command: "buzz-agent"andagent_command_override: "opencode"→ harness runs/opt/homebrew/bin/opencode acp, model served through a local LiteLLM proxy (OpenAI-compatible)Symptom: Agent produces a complete reply (confirmed in opencode's SQLite session store —
finish: "stop", text part present, 14k in / 58 out) but nothing ever renders in the channel. No partial message, no error — exactly the silent failure S1 describes.Root cause found — the publish tool is never attached:
Harness startup line (ACP log):
mcp_cmd=is empty, even though the agent'smanaged-agents.jsonentry declares"mcp_command": "buzz-dev-mcp".Live process env confirms it — the harness was launched with
BUZZ_ACP_MCP_COMMAND=empty, so this isn't a negotiation-time drop; the desktop app never passed the command through for this agent.Possible contributing factor: opencode's ACP handshake advertises
"mcpCapabilities":{"http":true,"sse":true}— no stdio — andbuzz-dev-mcpis a stdio command. If capability negotiation is what gates the stdio MCP launch, that would explain the emptymcp_cmdeven when the config declares it.Consequence: the session had zero tool calls (parts are only
step-start → text → step-finish), so per the harness contract ("a result… exists only if you published it"), the reply is discarded. 0.5.14 has no fallback to recover it.Workaround attempted: added
buzz-dev-mcpas a local MCP server in opencode's own config (opencode.json) so the agent would inherit the harness'sBUZZ_PRIVATE_KEY/BUZZ_AUTH_TAG/BUZZ_RELAY_URLenv — publish tool still didn't fire / still no delivery.This seems to be the gap your content-delivery fallback covers — happy to provide full ACP logs,
managed-agents.jsonredacted, or run any debug builds if useful. Also worth noting: with an override agent, the bundledbuzz-agentwrapper (which bootstraps MCP) is bypassed entirely, andgooseisn't installed on the machine, so the only paths into MCP are the harness's own (empty)mcp_cmdor manual agent-side config.ochianciprian95 commentedon Aug 17, 2026
Reproduced on desktop 0.5.14 (macOS, darwin 25.5), goose 1.45.0, with provider
openaiand secrets resolving correctly, so this failure class is wider than the openai-compat secrets path described above.Setup: three managed goose agents, each pointed at a local OpenAI-compatible bridge through agent env vars (
OPENAI_HOST=http://127.0.0.1:<port>,OPENAI_API_KEYandOPENAI_COMPAT_API_KEYset to the placeholder the bridge accepts).respond_to=owner-only, parallelism 1, Block-hosted relay.On every owner @mention, for all three agents, in a private channel and in a fresh public one:
:speech_balloon:) to the mention, so the key, the relay connection and event publishing work.~/.local/state/goose/logs/llm_request.0.jsonlmarkeduserVisible. Desktop's per-agent activity feed shows the same final text.buzz_acpinfo logs stay frozen aftersubscribed to channel ...: no error, noagent_returned, nothing. This matches the note above that a frozen log is not a falsifier.Two details that may narrow it down:
openaiwe hit the-32603 Internal errorsurfaced as a Codex message; that part matches Goose harness: Buzz injects GOOSE_PROVIDER=openai-compat (invalid), overriding a working Goose config; surfaces as a Codex error #4569 (GOOSE_PROVIDER=openai-compatinjection) and went away with the provider change.In our traces goose never calls a publish tool during the mention turn; the final text only comes back as ACP turn output. If the harness content-delivery fallback is what should publish it in that case, the fallback does not fire on 0.5.14.
iroiro147 commentedon Aug 27, 2026
@ochianciprian95 Your reproduction widens this materially and I should have said so sooner — apologies for the week of silence.
The important part is that you hit it with provider
openaiand secrets resolving correctly. The earlier reports in this thread all involved theopenai-compatsecrets path, which made a secrets-resolution bug the natural reading. Three agents failing identically on a Block-hosted relay, with the ack reaction publishing but no message, rules that out: the key, the relay connection and the publish path are all demonstrably working, so whatever drops the reply sits after generation and before the message publish, independent of provider family.That is the same failure class @Pep939 reported on the first-party
buzz-agentruntime and @blabj on an opencode override — which together point at the harness publish path rather than any one ACP implementation, exactly as @Pep939 argued.My open PR here (#4284) addresses the secrets path specifically, so on the evidence you have posted it will not fix your case. I would rather say that plainly than let it look like coverage.
I am not going to open a second PR at this repository — there are already more open from me than is reasonable for one reviewer, and the harness publish path is a bigger change than I should be adding to that pile uninvited. If a maintainer would rather I did take it, say so and I will.
One thing that would sharpen the diagnosis if you have the appetite: whether the agent's own runtime shows a completed response (goose keeps one in its session store, as @blabj confirmed for opencode). If the reply exists there but never reaches the channel, that isolates it to publish rather than generation, on a third runtime, and makes the case hard to dismiss.