Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 changes: 1 addition & 1 deletion crates/buzz-acp/src/base_prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ All replies and delegations — including task assignments to other agents — g
### General

- Respond promptly to @mentions. Be direct — no preamble. Name what you did, what you found, or what you need.
- **If your turn produced anything worth knowing, you MUST publish it.** Use `buzz messages send`. Your reasoning and tool calls are invisible — a result, an answer, a deliverable, a decision, a blocker, or a question you need answered exists only if you published it. Work or an answer that someone asked you for always counts. Ending that kind of turn without a message is a silent failure.
- **If your turn produced anything worth knowing, you MUST publish it.** Use `buzz messages send`. Session text is not delivered to the channel — only a successful `buzz messages send` posts a reply. Your reasoning and tool calls are invisible — a result, an answer, a deliverable, a decision, a blocker, or a question you need answered exists only if you published it. Work or an answer that someone asked you for always counts. Ending that kind of turn without a message is a silent failure.
- **If a human asked you something, you MUST reply to them** — even if the reply is only that you have nothing to add or nothing to do. Never leave a person waiting on you.
- **Otherwise, publishing is optional and silence is usually correct.** When a message leaves you nothing new to contribute, end the turn without publishing. That is a success, not a failure.
- **After a context compaction or session restart, resume silently** — rebuild state from your todos, memory, and the thread, and never post a message announcing the compaction, summarizing what was lost, or asking how to proceed.
Expand Down
10 changes: 10 additions & 0 deletions crates/buzz-acp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3560,6 +3560,16 @@ mod agent_draft_prompt_tests {
assert!(prompt.contains("single-quoted shell strings preserve `\\n` literally"));
assert!(prompt.contains("buzz messages send ... --content -"));
}

#[test]
fn shared_base_prompt_states_session_text_is_not_delivered() {
let prompt = include_str!("base_prompt.md");
assert!(
prompt.contains("Session text is not delivered to the channel"),
"base prompt must state that session text is not a channel reply"
);
assert!(prompt.contains("buzz messages send"));
}
}

fn default_heartbeat_prompt() -> String {
Expand Down
18 changes: 12 additions & 6 deletions crates/buzz-acp/src/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1148,9 +1148,10 @@ pub(crate) fn format_event_block(
/// top level.
fn append_reply_instruction(s: &mut String, event_id: &str) {
s.push_str(&format!(
"\nIMPORTANT: For ordinary replies in this turn, use `--reply-to {event_id}` \
on `buzz messages send` so the conversation stays threaded. \
If the human explicitly asks for a channel-root, top-level, \
"\nIMPORTANT: Your session text is NOT delivered to the channel — publish \
with `buzz messages send`. For ordinary replies in this turn, use \
`--reply-to {event_id}` on `buzz messages send` so the conversation stays \
threaded. If the human explicitly asks for a channel-root, top-level, \
or broadcast post, send that message without `--reply-to`. \
If the requested destination is ambiguous, ask before sending."
));
Expand All @@ -1163,9 +1164,10 @@ fn append_reply_instruction(s: &mut String, event_id: &str) {
/// choice open) prevents replying into a stale/unrelated prior thread.
fn append_new_thread_reply_instruction(s: &mut String, event_id: &str) {
s.push_str(&format!(
"\nIMPORTANT: This is a new top-level message. For ordinary replies in \
this turn, use `--reply-to {event_id}` on `buzz messages send` — the \
triggering message is the thread root. Do NOT reply into any other \
"\nIMPORTANT: Your session text is NOT delivered to the channel — publish \
with `buzz messages send`. This is a new top-level message. For ordinary \
replies in this turn, use `--reply-to {event_id}` on `buzz messages send` \
— the triggering message is the thread root. Do NOT reply into any other \
(older) thread. If the human explicitly asks for a channel-root, \
top-level, or broadcast post, send that message without `--reply-to`."
));
Expand Down Expand Up @@ -3889,6 +3891,10 @@ mod tests {
prompt.contains(&format!("--reply-to {root_id}")),
"human-facing thread reply should anchor to the thread root"
);
assert!(
prompt.contains("session text is NOT delivered to the channel"),
"reply instruction must state that session text is not delivered"
);
assert!(
prompt.contains("For ordinary replies in this turn"),
"channel thread reply should describe reply-to as the default"
Expand Down
9 changes: 7 additions & 2 deletions desktop/src-tauri/src/commands/agent_models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -639,8 +639,13 @@ async fn discover_anthropic_models(
return Ok(None);
}

let api_key = env_or_process_value(env, "ANTHROPIC_API_KEY")
.ok_or_else(|| "config: ANTHROPIC_API_KEY required".to_string())?;
// Claude Code users typically authenticate via OAuth (`claude setup-token`),
// not ANTHROPIC_API_KEY. Missing key must fall through to `buzz-acp models`
// rather than abort discovery with a hard error (#2581).
let api_key = match env_or_process_value(env, "ANTHROPIC_API_KEY") {
Some(key) => key,
None => return Ok(None),
};
let redaction_env = redaction_env_with_value(env, "ANTHROPIC_API_KEY", &api_key);
let url = anthropic_models_url_for_discovery(env);
let mut models = Vec::new();
Expand Down
12 changes: 10 additions & 2 deletions desktop/src/features/profile/ui/ProfileAvatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,12 @@ export function ProfileAvatar({

// Compute the live (proxied) source. Failures are tracked per resolved URL so
// the poster and hover animation can recover independently.
const liveSrc = baseUrl ? rewriteRelayUrl(baseUrl) : null;
// Prefer the cached data URL while the media proxy is still warming up —
// `rewriteRelayUrl` returns `buzz-media://` until the port is known, and
// that scheme fails in the WebView (#2665).
const rewritten = baseUrl ? rewriteRelayUrl(baseUrl) : null;
const proxyPending = rewritten?.startsWith("buzz-media:") ?? false;
const liveSrc = proxyPending ? null : rewritten;
const [failedSrc, setFailedSrc] = React.useState<string | null>(null);
const liveFailed = liveSrc !== null && failedSrc === liveSrc;

Expand All @@ -55,7 +60,10 @@ export function ProfileAvatar({
const src = liveFailed
? (avatarDataUrl ?? undefined)
: (liveSrc ?? avatarDataUrl ?? undefined);
const shouldShowFallback = src === undefined || (!animated && liveFailed);
// Don't force the monogram while we still have a usable image source
// (including the cached data URL after a live load failure).
const shouldShowFallback =
src === undefined || (!animated && liveFailed && !avatarDataUrl);

return (
<Avatar
Expand Down
6 changes: 6 additions & 0 deletions desktop/src/shared/lib/mediaUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,12 @@ export function resetMediaCaches(): void {
cachedRelayOrigin = null;
notifyRelayOriginListeners();
}
// Kick a fresh port/origin fetch immediately so the next render does not
// linger on `buzz-media://` (which WebViews cannot load) after a community
// switch or restart (#2665).
if (typeof window !== "undefined") {
portPromise = fetchProxyPort();
}
}

/**
Expand Down