Repository navigation
feat(agents): share agents to the community catalog and export snapshots - #1649
Conversation
ba8442c to
763c747
Compare
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Carl, an automated reviewer, commenting via Wes’s GitHub account.
Adversarial review of 763c747 across four lanes: relay/catalog contract, secret/import boundary, native save + file format, and dialog UI/state. Hosted checks were green at this head (35 pass, 2 skipped).
Verdict: one recommended blocker, four should-fix. The false-success unshare is the one I'd hold merge on. When it happens, the UI tells the owner their agent is hidden while it stays public.
Held up under review: each runtime's exportShared builds an explicit allowlist with no config spread. Picture URLs reject userinfo, non-HTTPS, query and fragment. file_download is granted to the main local-origin webview only, opens files with create_new, sets quarantine/zone marking before it writes, and caps size at 10 MiB. Import size gates run before the file is read. The snapshot tEXt chunk sits right after IHDR with a valid CRC. Artwork metadata is stripped.
Validation gaps: the unshare race was reproduced against a modeled relay ACK in a component test, not a live relay. The quoted-credential, collision-name and text-limit findings come from source and a regex check, not app runs.
Merge criteria: close the unshare race (inline). The should-fix items can land here or as a tracked follow-up, at the author's call.
Optional nits, no inline threads:
file_downloadaccepts any extension even though it exists only for agent snapshots. Limiting it to.agent.png/.agent.jsonis least privilege. Its errors also say "Could not save media".fileNamekeeps only ASCII, soドーナツbecomes-.agent.png. The native validator already accepts Unicode safely; its own test usesAda 🤖.agent.png.- JSON export has no size check. Over 5 MiB the app's own importer refuses it, and over 10 MiB the native save fails with a bare "Invalid file".
- block/buzz locked cards (
buzz-agent-snapshot-encrypted) get "Unsupported snapshot format." A message saying the card is locked would read better.
763c747 to
fa381e6
Compare
|
🤖 Thanks for the review. I've replied on each inline thread, and the blocker and all four should-fix items are addressed in fa381e6, which is rebased on main. On the nits:
The PR is back in draft until the new behavior has been tested by hand again. |
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Carl, an automated reviewer, commenting via Wes’s GitHub account.
Re-review at ed78e3fda. The rebase onto main left the five earlier commits patch-identical (git range-diff), so this covers fa381e6bf and ed78e3fda.
Verdict: no remaining blockers. All five earlier findings are resolved or reasonably declined:
- False-success unshare (blocker): resolved. Every publish is now followed by a strong
catalogHeadread, and success is reported only whenisShared(head) === shared. A failed re-read keeps the last confirmed head. The new tests are falsifiable: withAgentShareDialog.tsxrestored fromc91dad407, all three new dialog tests fail. - Credential guard: resolved. Quoted labels and values,
nsec1(bech32 charset, 58 characters) andgh[pousr]_tokens are now caught. Keeping block/buzz's Share copy verbatim is a reasonable call. - Collision naming: resolved.
Ada (1).agent.pngis now produced and covered by a test.file_downloadnow also accepts only.agent.png/.agent.jsonnames. - Artwork stall: resolved within bounds. The wait is capped at 5 s, and the image load is cleared on timeout. Not using
"small"is justified, since it's a 320 px JPEG without transparency. - Portable text: resolved.
checkTextmatches block/buzzvalidate_agent_definition_text: the same 128-character and 64 KiB limits, the sameDefault_Ignorable_Code_Pointranges (compared line by line), and the same emoji exceptions for variation selectors and ZWJ. It runs on share, export and file import.
Validation: at ed78e3fda, agent-share.test.ts and AgentShareDialog.test.tsx pass (22 tests), and hosted checks show 35 pass, 2 skipped. Nothing was exercised against a live relay or in the app.
Optional, no change needed for merge: Marketplace (catalog) import still shows definitions without checkText. That path predates this PR, but it now shares the import dialog with file import, so applying the same check there would make the two consistent.
ed78e3f to
5456d80
Compare
wesbillman
left a comment
There was a problem hiding this comment.
🤖 No new changes requested. Star Lord’s automated source review via Wes’s account, integrating independent Rocket, Mantis and Groot lanes, found no actionable regressions in the native saved-path notice or multi-community rebase integration; the prior review’s accepted resolutions remain intact.
Reviewed head 5456d8027815a15422d667559ec3634292cbc4ee against base 72a8362de9f385900bbd61e57bce26822fe01efc, comparing the follow-up with ed78e3fda. Source-only: tests, CI and desktop/relay behavior were not exercised or verified; rendered focus and accessibility remain unverified, and the description still records pending human testing of the fixes and saved-path notice.
5456d80 to
57653d6
Compare
4ec765a to
c393e7e
Compare
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Three deletion fixes are requested inline: signer admission, durable hosted retry, and timestamp-safe catalog cleanup (currently masked by the signer rejection).
Also resolve the missing repository-required DCO sign-off on inherited commit d90d843b32db3d9c125957fe7766fe34e4012b6c, or exclude that obsolete ancestor when updating the stack. The inherited config-authority issue remains tracked in #1226; this review does not duplicate it. Prior accepted share/export resolutions remain intact.
Star Lord’s automated source review via Wes’s account, integrating Rocket, Mantis and Groot. Head 57653d6ae09e182288473c8093359b8eed219784; base c393e7eb11dd1190b114c6f061686a39aecaea6d. Source-only: no tests, CI, app or live-relay validation; keyboard/focus and native workflow acceptance remain unverified.
8d74771 to
2cfc0de
Compare
57653d6 to
d65541a
Compare
add exportShared to agent types, the inverse of importShared, for claude, codex, goose and pi. an agent card menu gains share: a share to catalog switch publishes or replaces the owner-signed kind 30175 head addressed by the agent pubkey, and export agent downloads a buzz-agent-snapshot v1 .agent.json. import an agent reads .agent.json and .agent.png snapshots into the shared import dialog. both paths use an allowlisted definition and refuse credential-like text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Kalvin Chau <kalvin@block.xyz>
- show agent instructions in the import dialog before confirming - reconcile an unanswered catalog publish by reading the head again - retry the catalog read after an initial failure - refuse oversized snapshot files by size before reading them - move file import out of the identity-preserving copy section Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Kalvin Chau <kalvin@block.xyz>
- omit a picture whose query or fragment could carry a token - check the retained picture for credentials Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Kalvin Chau <kalvin@block.xyz>
…n desktop - add a file format choice (png or json) to the share dialog's export - encode .agent.png from the picture redrawn at 512x512, or a transparent pixel, with the manifest in a buzz_agent_snapshot text chunk - save exports through a file_download command on desktop, reusing the collision-safe, quarantine-marked downloads writer, instead of a blob link Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Kalvin Chau <kalvin@block.xyz>
- bound the file name stem and suffix windows device names such as con, which the native save rejects Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Kalvin Chau <kalvin@block.xyz>
…snapshot rules re-read the catalog head after every publish so an accepted unshare that loses to a newer head reports failure. catch quoted credential labels, nsec1 and github tokens. validate names and instructions as block/buzz validate_agent_definition_text does on export and import. bound artwork loading to 5 seconds. keep the .agent. suffix on download collisions and limit file_download to snapshot names. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Kalvin Chau <kalvin@block.xyz>
an accepted publish is not the head until the relay confirms it. when the strong re-read fails, keep the last confirmed head and report the failure instead of trusting the accepted event. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Kalvin Chau <kalvin@block.xyz>
file_download returns the saved path, and the share dialog names it in the export notice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Kalvin Chau <kalvin@block.xyz>
… on delete shareable() refuses an agent with a configProblem, whose synced settings and instructions are left out of config, so share and export never publish an empty system_prompt. catalogContent() emits acp_command "buzz-acp", as nip-ap requires of shared heads. delete sends a nip-09 deletion of the owner kind 30175 head addressed by the agent pubkey in each community the agent leaves, local and hosted; remove from one community keeps its share. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: peon <9ac6794b000690b7e814eb1805ad32405d0bec7d52838de3a86cf967565dacc0@buzz.block.builderlab.xyz> Signed-off-by: Kalvin Chau <kalvin@block.xyz>
d65541a to
6d0cb45
Compare
…ng it the native signer and the browser broker admit only kind 30177 coordinate deletions, so the kind 5 for the 30175 head was refused and every delete failed. a wall-clock deletion also misses a head dated ahead of this clock. delete now republishes a shared head without the tag through the catalog path both signers admit, superseding it at max(now, head + 1), and goes on only once a fresh read no longer finds it shared. hosted delete unshares before removing its 30177 record, so a failure keeps it listed to retry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: peon <9ac6794b000690b7e814eb1805ad32405d0bec7d52838de3a86cf967565dacc0@buzz.block.builderlab.xyz> Signed-off-by: Kalvin Chau <kalvin@block.xyz>
wpfleger96
left a comment
There was a problem hiding this comment.
🤖 reviewed at a3b9ed2. the Delete rework looks right at source: unshare goes through the admitted catalog write, supersedes the old head, re-reads it, keeps the hosted listing when cleanup fails, and skips the write when nothing is shared. I haven't run it against a live relay. the export allowlist, the snapshot/text contract against block/buzz validate_agent_definition_text, and the native download handling also held up.
two things I'd want fixed before merge (file import going unreachable, no way to update a share), plus a design question on Remove and two small ones. inline below.
import an agent stays in the new agent menu, and the first-agent screen offers import from a file, when discovery finds nothing, since a file import doesn't depend on it. the share dialog offers update share while the shared head differs from what the agent would share now, publishing it still shared. every departure unshares, as nothing in that community lists the agent to unshare from afterwards. the shared import dialog runs checktext and refuses import on failure, and the credential message fits sharing too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: peon <9ac6794b000690b7e814eb1805ad32405d0bec7d52838de3a86cf967565dacc0@buzz.block.builderlab.xyz> Signed-off-by: Kalvin Chau <kalvin@block.xyz>
wpfleger96
left a comment
There was a problem hiding this comment.
🤖 re-reviewed at 9974d7b. all five comments from my last review are addressed: Import an agent stays in the menu and the first-agent screen gets an Import from a file tile when discovery finds nothing, Update share republishes the current definition still shared (superseding the head, no unlisted gap), every departure now unshares before the record goes, the shared import dialog runs checkText and disables Import on failure, and the credential message fits sharing. the fix commit doesn't touch the Delete/unshare path from Wes's threads, and I didn't find anything new. LGTM
…ted-setup * origin/main: (24 commits) feat(agents): share agents to the community catalog and export snapshots (#1649) feat(navigation): dialogs never become Back/Forward destinations (#1696) fix(builderlab): sign in when finishing a hosted agent's setup (#1768) fix(messages): link #name prose to open channels the directory has named (#1706) feat(agents): keep the Mac awake while agents work (#1718) perf(panels): skip layout for covered workspace tabs (#1542) feat(composer): offer model and effort in a DM with an agent without a mention (#1621) test(workflows): wait for dialog initial focus before pasting YAML in WorkflowEditor test (#1705) fix(builderlab): show all captured activity without a conversation toggle (#1764) feat(agents): add a UI for Attention watches (#1663) Keep thread reply branches one level deep (#1751) fix(messages): keep table scrollbars inside webkit message rows (#1677) feat(agents): sync agent config through config channels (#1226) fix(agents): offer a new hosted agent in Solo chats right away (#1759) test(usage): make UsagePage read-window test DST-safe (#1668) fix(panels): scope split motion selectors to layout owners (#1681) feat(agents): simplify imports and clarify community scope (#1660) fix(pi): stop posting replies the agent did not send (#1758) feat(identity): support a local debug restore helper (#1517) test(perf): cover rich thread open close and reopen (#1683) ... Signed-off-by: Riley Crane <rileycrane@squareup.com>
…into rileycrane/bestie-hourly-wake * origin/rileycrane/bestie-hosted-setup: (25 commits) Give Bestie's test fixture the native keepAwake that main now requires feat(agents): share agents to the community catalog and export snapshots (#1649) feat(navigation): dialogs never become Back/Forward destinations (#1696) fix(builderlab): sign in when finishing a hosted agent's setup (#1768) fix(messages): link #name prose to open channels the directory has named (#1706) feat(agents): keep the Mac awake while agents work (#1718) perf(panels): skip layout for covered workspace tabs (#1542) feat(composer): offer model and effort in a DM with an agent without a mention (#1621) test(workflows): wait for dialog initial focus before pasting YAML in WorkflowEditor test (#1705) fix(builderlab): show all captured activity without a conversation toggle (#1764) feat(agents): add a UI for Attention watches (#1663) Keep thread reply branches one level deep (#1751) fix(messages): keep table scrollbars inside webkit message rows (#1677) feat(agents): sync agent config through config channels (#1226) fix(agents): offer a new hosted agent in Solo chats right away (#1759) test(usage): make UsagePage read-window test DST-safe (#1668) fix(panels): scope split motion selectors to layout owners (#1681) feat(agents): simplify imports and clarify community scope (#1660) fix(pi): stop posting replies the agent did not send (#1758) feat(identity): support a local debug restore helper (#1517) ... Signed-off-by: Riley Crane <rileycrane@squareup.com>
Adds sharing to the community catalog and
buzz-agent-snapshot.agent.png/.agent.jsonexport and import to agents2, matching the earlier Buzz and block/buzz workflows and formats.Changes
src/features/agents/agent-share.ts): a single allowlist builds both the share and the export. It includes the name, instructions, runtime, provider, model and an HTTPS picture without a query or fragment. Keys, workspace, environment, memory and every other setting are left out.sk-,nsec1and GitHub tokens, and quoted or unquotedapi_key,access_token,secretandpasswordvalues.validate_agent_definition_texton export and import: names up to 128 characters, instructions up to 64 KiB, and no control or invisible characters other than newlines, tabs and the joiners and selectors inside emoji.exportShared, the inverse ofimportShared. It maps each config to the catalogruntime,providerandmodel.AgentShareDialog): opened from the agent card's Share action.["shared","true"]. It supersedes the head returned by a strong read."acp_command": "buzz-acp", as NIP-AP requires of shared heads.configProblem: without a usable synced copy its synced settings and instructions are left out ofconfig, so it would publish an emptysystem_prompt.{name}.agent.png(default) or{name}.agent.json, chosen by File format. The PNG is the agent's picture redrawn at 512×512, or a transparent pixel when the picture is missing or takes more than 5 seconds to load. The manifest is in abuzz_agent_snapshottEXt chunk, and none of the artwork's metadata is kept.max(now, head + 1), so a head dated ahead of this clock is replaced too.file_download,src-tauri/src/relay.rs): desktop saves exports to Downloads through the same collision-safe, quarantine-marked writer media downloads use, instead of ablob:link, which WKWebView reports as a sandbox failure..agent.pngor.agent.jsonname and at most 10 MiB of raw bytes. Only the main window is granted it.Ada (1).agent.png, so its suffix still marks it as a snapshot..agent.jsonand.agent.pngsnapshots. Import an agent stays in the New agent menu, and the first-agent screen offers Import from a file, even when discovery finds nothing. PNG snapshots carry a base64 manifest in abuzz_agent_snapshottEXt chunk.docs/agents.mddocuments the sharing and export boundary.Existing UI copy from earlier Buzz and block/buzz is reused throughout.
Verification
bin/pnpm checkandbin/pnpm typecheckpass.configProblemrefusal, the explicitacp_command, unsharing on Delete (a head dated ahead of this clock, a newer shared head that wins, nothing shared), a hosted Delete whose unshare fails and keeps the agent listed, and the Delete order for local and hosted agents (Remove from one community sends no share deletion).file_downloadname/extension/body bounds, download-name and collision naming (includingAda (1).agent.png), and the webview permission matrix..agent.jsonexport, then file importNot covered