Skip to content

fix(buzz-cli): deliver documents as file cards instead of broken images - #7479

Open
kaminai0000 wants to merge 1 commit into
block:mainfrom
kaminai0000:fix/cli-generic-file-attachments
Open

fix(buzz-cli): deliver documents as file cards instead of broken images#7479
kaminai0000 wants to merge 1 commit into
block:mainfrom
kaminai0000:fix/cli-generic-file-attachments

Conversation

@kaminai0000

Copy link
Copy Markdown

Summary

buzz-cli is both stricter and less correct than the relay it talks to when
attaching a non-image file, so an agent cannot deliver any document to a
conversation. Two defects, neither visible to the sender.

Fixes #3083
Fixes #7324

1. Uploads were rejected client-side (#3083)

ALLOWED_MIMES listed only four image types plus video/mp4, so anything else
failed with unsupported file type: … before the request reached the relay.

The relay's /upload route already sniffs documents, archives, text and data
onto its generic-file path (buzz-media::validation, a deny-list that blocks
only active content and executables) and serves them with
Content-Disposition: attachment. Desktop users could attach a PDF; agents
could not.

2. Everything non-video was announced as an image (#7324)

The markdown embed came from a two-way branch: video/* got ![video](…) and
everything else got ![image](…). A document was therefore announced to the
client as an image and rendered as a permanently broken image placeholder —
while the send returned accepted: true, so the failure only ever appeared in
the recipient's client.

Generic files now use the same plain [filename](url) form Buzz Desktop emits
(imetaMediaMarkdown.ts::formatImetaMediaLine), which the renderer recognises
as a non-media blob and upgrades to a download card. The imeta tag carries
filename so the card has a label instead of the blob hash, and markdown
metacharacters in the label are escaped so a name like a].pdf cannot break
the link.

Testing

The embed choice is extracted into format_media_markdown so tests bind the
production branch rather than a copy of it. Reverting either fix turns the new
tests red — verified by re-introducing the ![image](…) catch-all, which fails
three of them.

  • cargo test -p buzz-cli --lib — 469 passed
  • cargo fmt --check -p buzz-cli, cargo clippy -p buzz-cli --all-targets — clean

Reproduced end to end against a self-hosted relay: a headless buzz-acp agent
generating an .xlsx could not attach it at all before this change, and
delivers it as a working download card after.

🤖 Generated with Claude Code

`buzz messages send --file` and `buzz upload file` were both stricter and
less correct than the relay they talk to, so an agent could not attach any
non-image artifact to a conversation.

Two defects, both invisible to the sender:

1. `ALLOWED_MIMES` listed only 4 image types plus `video/mp4`, rejecting
   uploads client-side with `unsupported file type: …` before the request
   reached the relay. The relay's `/upload` route already sniffs documents,
   archives, text and data onto its generic-file path (`buzz-media::validation`,
   a deny-list that blocks only active content and executables) and serves
   them as downloads. Desktop users could attach a PDF; agents could not.

2. Once past that gate, the markdown embed was built from a two-way branch —
   `video/*` got `![video](…)` and *everything else* got `![image](…)`. A
   document was therefore announced to the client as an image and rendered as
   a permanently broken image placeholder. The send still returned
   `accepted: true`, so the failure only ever surfaced in the recipient's
   client.

Generic files now use the same plain `[filename](url)` form Buzz Desktop
emits (`imetaMediaMarkdown.ts`), which the renderer recognises as a non-media
blob and upgrades to a download card, and the `imeta` tag carries `filename`
so the card has a label instead of the blob hash. Markdown metacharacters in
the label are escaped so a name like `a].pdf` cannot break the link.

The embed choice is extracted into `format_media_markdown` so the branch is
covered directly rather than through the async send path; reverting either
fix fails the new tests.

Fixes block#3083
Fixes block#7324

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: kaminai0000 <kaminai0000@gmail.com>
@kaminai0000
kaminai0000 requested a review from a team as a code owner September 8, 2026 09:52
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown

🔐 Codex Security Review

Status: review required for the current range.

The current range is 3c7f288c60d67df78577b237e27c3dfc8831aaa1...ebe53f38b2513cf19f6a4aa5270532256c35077a.
A new review must complete for this exact range. When manual authorization
is required, a Block organization member must comment exactly
@buzz-security-review ebe53f38b2513cf19f6a4aa5270532256c35077a to authorize a new review.
Any previous review applies only to its recorded range.

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

Labels

None yet

Projects

None yet

1 participant