镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

Desktop goose ACP agents silent or flaky with custom OpenAI-compatible provider (hosted models) #4233

Open

Description

@coldcanuk

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

  1. Agent harness is up (presence online, pool ready, subscribed to channel).
  2. Owner @mention produces typing/activity (or nothing visible).
  3. No kind:9 channel reply (thread or top-level).
  4. Goose session may still show a full assistant turn (text / write tools) in session DB.
  5. Harness logs may show either:
    • agent_returned / JSON-RPC -32603 Internal error with goose data Provider 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/debug rebuilds 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=1 is the required switch so goose can load ~/.config/goose/secrets.yaml when 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 like GOOSE_ACTVITE_API_KEY in 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 (toolRequest count = 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)

  1. 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).
  2. 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).
  3. Observe: turn error / -32603 Provider not set or fix secrets and continue.
  4. With secrets fixed but without harness content-delivery fallback: owner @Agent write a short sentence → session has assistant text → zero channel kind:9.
  5. With content-delivery fallback binary: same ordinary wording → exactly one kind:9.

What we believe should change in product

  1. Document Desktop goose + custom provider requirements (GOOSE_DISABLE_KEYRING, provider key env names, secrets.yaml vs keyring under GUI).
  2. 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=1 for goose-runtime agents on hosts without a working system keyring would cover the common edge case.
  3. Ship content-delivery fallback in buzz-acp as the default for channel/DM mention turns so self-hosted / non-tool-calling models are not second-class for channel visibility.
  4. Spawn resolution (dev): prefer absolute/user-installed harness, or stop resolving bare buzz-acp exclusively to workspace target/debug in a way that silently reverts product fixes after every full Desktop rebuild. Absolute acp_command should 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

  1. extraknown-alec commented on Aug 2, 2026

    @extraknown-alec

    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.

  2. iroiro147 commented on Aug 2, 2026

    @iroiro147
    Contributor

    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:

    • C (workspace clobber): smallest, most deterministic. Single-point change in resolve_workspace_command (desktop/src-tauri/src/managed_agents/discovery.rs) — add a bool argument or a config knob that, when acp_command is an absolute path that already exists on disk, skips the workspace-target resolution entirely. Acceptable behavior on tauri dev/packaged builds. ~5 LOC + tests.
    • A (custom-provider secrets): mostly docs + one harness tweak. Add a section to docs/agents/goose.md covering GOOSE_DISABLE_KEYRING=1 + provider-specific env names for GUI spawns, and (optionally) warn in the agent add-wizard when GOOSE_PROVIDER=custom_* is set without GOOSE_DISABLE_KEYRING. ~20 LOC docs + optional warn.
    • B (content-delivery fallback): the biggest behavior change. The KISS deterministic version (post assistant text once if no successful publish tool) is probably right, but it interacts with PUBLISH ONCE guarantees, typing indicator state, and undo/redo in the timeline. Needs a design conversation, not a one-PR commit.

    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.

  3. coldcanuk commented on Aug 2, 2026

    @coldcanuk
    Author

    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

    Item Value
    Fork https://github.com/coldcanuk/buzz
    Worktree (host) /opt/repo/worktrees/wt-acp-s1-auto-publish-20260801
    Branches fizz/acp-s1-auto-publish and fizz/acp-once-publish-stop (same tip)
    HEAD 1d0708a9a
    Fork main Fast-forwarded to the same tip for local deploy tracking

    Commits on the branch (rebased onto current block/buzz main):

    1. bca8bab78 — fix(acp): deliver plain-text replies when the model skips the send tool
    2. f68a1d7ae — fix(acp): confirm message delivery from tool outcome, not input text
    3. f5e5ecb89 — feat(acp): S1 detect MCP buzz_messages_send tool names
    4. 1d0708a9a — feat(acp): once-publish-per-trigger stop after first confirmed send

    Pull 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

    What this does not fix alone

    Happy to iterate on review feedback on #4191.

  4. Pep939 commented on Aug 4, 2026

    @Pep939

    Independent reproduction of this failure class on the first-party buzz-agent runtime — not goose — which suggests the gap is in the harness publish path rather than in any particular third-party ACP implementation.

    Environment

    Piece Setup
    Host Buzz Desktop 0.5.2 (release AppImage, not tauri dev)
    Relay Hosted wss://<name>.communities.buzz.xyz (also reproduced against a self-hosted relay)
    Runtime buzz-agent via buzz-acp harness
    Provider openai-compat → Ollama on 127.0.0.1:11434
    Model ornith:9b (local, 6.4 GB, fully GPU-resident)
    Access respond_to default; owner @mention in an open channel

    The controlled comparison

    One message @-mentioning three agents at once, same channel, same instant:

    Agent Runtime Result
    A claude-agent-acp posts a kind:9 thread reply ✅
    B codex-acp posts a kind:9 thread reply ✅
    C buzz-agent + local Ollama no channel event ❌

    Because 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:

      curl -s http://127.0.0.1:11434/v1/chat/completions -d '{"model":"ornith:9b","messages":[{"role":"user","content":"Say exactly: hello from ornith"}]}'
      → keys: ['role', 'content', 'reasoning']
        content:   'Hello from Ornith!'
        reasoning: 'The user wants me to say "hello from ornith"...'
        finish_reason: stop
      

      content is populated and distinct from reasoning, so crates/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 buzz PATH 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 buzz resolves 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:

    05:06:37  connected to relay at wss://<redacted>
    05:06:37  discovered 2 channel(s) / subscribed / presence set to online
    05:06:37  buzz_agent: session/set_model: model overridden model_id=ornith:9b
    05:08:16  rmcp::service: input stream terminated
    05:08:16  rmcp::service: serve finished quit_reason=Closed
    05:08:16  buzz_acp: shutting down
    05:08:16  buzz_acp: shutdown: waiting for in-flight prompts
    05:08:16  buzz_acp: presence set to offline
    05:08:16  buzz_acp: buzz-acp stopped
    

    The assistant text was visible in the desktop at 05:06:45 — ~90 s before this shutdown. The same log shows 9 buzz-acp starting lines 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 prompts is 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.

  5. blabj commented on Aug 16, 2026

    @blabj

    Additional data point: same silent-failure class with custom OpenAI-compatible provider but with an opencode-override agent — root cause appears to be mcp_cmd never being attached

    Setup: Buzz Desktop 0.5.14 (macOS), custom managed agent with agent_command: "buzz-agent" and agent_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:

    1. Harness startup line (ACP log):

      buzz-acp starting: ... agent_cmd=/opt/homebrew/bin/opencode acp mcp_cmd= idle_timeout=900s ...
      

      mcp_cmd= is empty, even though the agent's managed-agents.json entry declares "mcp_command": "buzz-dev-mcp".

    2. 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.

    3. Possible contributing factor: opencode's ACP handshake advertises "mcpCapabilities":{"http":true,"sse":true} — no stdio — and buzz-dev-mcp is a stdio command. If capability negotiation is what gates the stdio MCP launch, that would explain the empty mcp_cmd even when the config declares it.

    4. 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-mcp as a local MCP server in opencode's own config (opencode.json) so the agent would inherit the harness's BUZZ_PRIVATE_KEY/BUZZ_AUTH_TAG/BUZZ_RELAY_URL env — 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.json redacted, or run any debug builds if useful. Also worth noting: with an override agent, the bundled buzz-agent wrapper (which bootstraps MCP) is bypassed entirely, and goose isn't installed on the machine, so the only paths into MCP are the harness's own (empty) mcp_cmd or manual agent-side config.

  6. ochianciprian95 commented on Aug 17, 2026

    @ochianciprian95

    Reproduced on desktop 0.5.14 (macOS, darwin 25.5), goose 1.45.0, with provider openai and 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_KEY and OPENAI_COMPAT_API_KEY set 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:

    1. The agent publishes its ack reaction (:speech_balloon:) to the mention, so the key, the relay connection and event publishing work.
    2. The engine turn completes. The bridge logs the request, and the full final assistant text sits in ~/.local/state/goose/logs/llm_request.0.jsonl marked userVisible. Desktop's per-agent activity feed shows the same final text.
    3. No kind:9 reply reaches the channel, neither top level nor in a thread.
    4. buzz_acp info logs stay frozen after subscribed to channel ...: no error, no agent_returned, nothing. This matches the note above that a frozen log is not a falsifier.

    Two details that may narrow it down:

    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.

  7. iroiro147 commented on Aug 27, 2026

    @iroiro147
    Contributor

    @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 openai and secrets resolving correctly. The earlier reports in this thread all involved the openai-compat secrets 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-agent runtime 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions