Skip to content

feat(buzz-acp): publish the kind:10100 relay-agent directory entry - #6097

Closed
Schnitzel wants to merge 1 commit into
block:mainfrom
Schnitzel:acp/publish-agent-directory
Closed

feat(buzz-acp): publish the kind:10100 relay-agent directory entry#6097
Schnitzel wants to merge 1 commit into
block:mainfrom
Schnitzel:acp/publish-agent-directory

Conversation

@Schnitzel

Copy link
Copy Markdown

The problem

An agent run by buzz-acp never appears in Buzz Desktop's @ autocomplete, however healthy it is, and nothing anywhere reports why.

Desktop builds its mentionable set as managed agents ∪ relay agents (desktop/src/features/agents/knownAgentPubkeys.ts):

  • managed agents are the ones the desktop holds a key for and runs itself. An agent created in the app is in this set immediately and needs no relay event at all.
  • relay agents come from kind 10100, filtered by relayAgentCanRespondInChannel — the entry's channel_ids must contain the channel being typed in, and its respond_to must admit the person typing.

A harness-run agent can never be in the first set, so kind 10100 is its only route. buzz-acp never publishes one. channel_ids is then absent, agents_from_events defaults it to [], and the agent is eligible in no channel.

The failure is silent end to end: no client-side error, and a mention typed by hand goes out with no p tag, so it routes nowhere either. From the user's side a perfectly healthy bot simply cannot be mentioned.

examples/countdown-bot's README attributes mentionability to the role=bot self-add. In testing against a current relay and desktop that was not sufficient on its own; publishing kind 10100 was what made the agent appear.

The change

Publishes the entry from state the harness already holds — the channels it discovered and subscribed to, plus its own author gate — at startup, and again whenever a membership notification changes the channel set.

  • only subscribed channels are advertised, so the entry never offers a mention the agent would not actually receive
  • on membership change the channel set is re-read from the relay rather than trusted from local bookkeeping
  • best-effort: discoverability is not worth failing a startup or dropping a membership update over, so failures are logged and execution continues
  • kind 10100 is replaceable, so republishing supersedes rather than accumulating

Two new flags, so existing deployments that publish this entry themselves are unaffected:

Flag Env
--no-publish-directory BUZZ_ACP_NO_PUBLISH_DIRECTORY
--display-name BUZZ_ACP_DISPLAY_NAME

name is omitted when unset rather than guessed, so clients fall back to the agent's kind:0 profile name and then its npub, as they already do. respond_to_allowlist is emitted only in allowlist mode, mirroring the inbound author gate so the people who may prompt an agent are exactly the people whose client offers it.

Testing

  • 5 new unit tests on the entry body: only subscribed channels advertised, names positionally aligned with ids, allowlist only in allowlist mode, blank names omitted, output stable across builds (so a replaceable event is not rewritten without cause)
  • full buzz-acp suite: 783 passed, 0 failed
  • cargo clippy -p buzz-acp --all-targets clean, cargo fmt applied
  • built and exercised against a live self-hosted relay, where the published entry made a previously invisible agent mentionable

Question for maintainers

Kind 10100 does not appear in any NIP under docs/nips/, unlike the other agent-facing kinds (NIP-AP, NIP-AE, NIP-AO, NIP-AA). If it is intended to stay an internal convention I am happy to adjust; if it should be specified, I would be glad to draft it. Happy to change the default to opt-in if you would rather this not publish without being asked.

@Schnitzel
Schnitzel requested a review from a team as a code owner August 17, 2026 03:53
A harness-run agent cannot appear in Buzz Desktop's `@` autocomplete
today, however healthy it is, and nothing reports why.

Desktop builds its mentionable set as managed agents ∪ relay agents
(`desktop/src/features/agents/knownAgentPubkeys.ts`). Managed agents are
the ones the desktop holds a key for and runs itself, so an agent created
in the app qualifies immediately and needs no relay event at all. An
agent run by buzz-acp can never be in that set, which leaves the kind
10100 relay-agent directory entry as its only route — and buzz-acp never
publishes one. `relayAgentCanRespondInChannel` then fails on an absent
`channel_ids`, so the agent is silently offered nowhere.

The failure is quiet at every layer: no error client-side, and a mention
typed by hand goes out with no `p` tag, so it routes nowhere either.

This publishes the entry from state the harness already has — the
channels it discovered and subscribed to, plus its own author gate — at
startup and again whenever a membership notification changes the channel
set. Only subscribed channels are advertised, so the entry never offers a
mention the agent would not receive. On membership change the channel set
is re-read from the relay rather than trusted from local bookkeeping.

Publishing is best-effort: discoverability is never worth failing a
startup or dropping a membership update over, so failures are logged and
execution continues. Kind 10100 is replaceable, so republishing
supersedes rather than accumulating.

Two flags, both opt-out/opt-in rather than changing existing behaviour
for anyone who already publishes this entry themselves:

  --no-publish-directory / BUZZ_ACP_NO_PUBLISH_DIRECTORY
  --display-name / BUZZ_ACP_DISPLAY_NAME

`name` is omitted when unset rather than guessed, so clients fall back to
the agent's kind:0 profile name and then its npub, as they already do.
`respond_to_allowlist` is emitted only in allowlist mode, mirroring the
inbound author gate so the people who may prompt an agent are exactly the
people whose client offers it.

Tests cover the entry body: only subscribed channels are advertised,
names stay positionally aligned with ids, the allowlist appears only in
allowlist mode, blank names are omitted, and output is stable across
builds so a replaceable event is not rewritten without cause.

Signed-off-by: Michael Schmid <michael.schmid@amazee.com>
@Schnitzel
Schnitzel force-pushed the acp/publish-agent-directory branch from 1cf0cbf to 203770b Compare August 17, 2026 03:56
jhgaylor added a commit to managoat/fountain that referenced this pull request Aug 18, 2026
…ry entry (block/buzz#6097)

Buzz Desktop admits a non-owned agent to @-mention autocomplete only when the
relay carries a kind:10100 directory entry for it (channel_ids + respond_to);
nothing published one, so a hosted agent with respond_to=anyone was still
mentionable by its owner alone. block/buzz#6097 has buzz-acp publish the entry
at startup and on membership changes; the fork branch fountain-pin now carries
it on top of #6088/#6101/#6103/#6104. #776's repin condition gains #6097.

Refs #790.
jhgaylor added a commit to managoat/fountain that referenced this pull request Aug 18, 2026
…ry entry (block/buzz#6097) (#792)

Buzz Desktop admits a non-owned agent to @-mention autocomplete only when the
relay carries a kind:10100 directory entry for it (channel_ids + respond_to);
nothing published one, so a hosted agent with respond_to=anyone was still
mentionable by its owner alone. block/buzz#6097 has buzz-acp publish the entry
at startup and on membership changes; the fork branch fountain-pin now carries
it on top of #6088/#6101/#6103/#6104. #776's repin condition gains #6097.

Refs #790.
Schnitzel added a commit to Schnitzel/buzz-agent-deploy that referenced this pull request Aug 18, 2026
The skill offered two routes onto a closed relay — NIP-AA virtual
membership or `buzz-admin add-member` — as a preference with one stated
cost, "a second thing to remember to revoke". They are not
interchangeable, and the difference is invisible until you go looking.

Virtual membership makes the relay resolve the agent's NIP-OA owner and
write users.agent_owner_pubkey. Enrolment does not: check_relay_membership
matches direct membership first and returns before the auth tag is read,
so a valid proof is discarded on every connection and no owner is ever
recorded. That column gates NIP-AO kind 24200 observer frames, so an
enrolled agent has every frame rejected and its ACP activity tab is empty
forever. Enrolling the agent is what breaks it.

Nothing reports this. The relay logs nothing and only increments
buzz_events_rejected_total{reason="auth"}, buzz-acp never surfaces the
OK=false, and the agent goes on reporting "relay observer enabled" with a
resolved owner. Sent upstream as block/buzz#6098.

The component table also claimed NIP-AO was "handled by buzz-acp", which
is wrong in the sense that matters: the frames are opt-in behind
--relay-observer and neither env example set it. So an agent built by
following this skill had no activity feed for two independent reasons.

Adds the flag to both env examples, a section on the two gates with the
query that tells them apart, the relay-side mechanism in internals, and
troubleshooting rows — including that a teammate seeing an empty tab is
correct, since the frames are encrypted to the owner alone.

Also notes block/buzz#6097 against the every-deploy 10100 republish: if
the harness carries that, the workaround stops being load-bearing.
@Schnitzel

Copy link
Copy Markdown
Author

Flagging honestly that #6338 has substantially reduced the value of this PR, in case that changes whether you want it.

This publishes the kind 10100 directory entry from the harness, motivated mainly by the entry going stale: nothing republished it when the agent's channel set changed, so an agent added to a channel silently became unmentionable there. On our relay that bit four separate times in a week.

In v0.5.18 the relay-agent directory no longer takes channels from the entry at all — list_relay_agents_for_selection derives them from the relay's own kind 39002 rosters and then overwrites whatever the entry said:

agents.retain(|agent| member_agent_channel_ids.contains_key(&agent.pubkey));
for agent in &mut agents {
    agent.channel_ids = member_agent_channel_ids.get(&agent.pubkey).cloned().unwrap_or_default();
}

So the staleness problem this was written for is already solved, and better — membership now comes from the authority that owns it, rather than from a snapshot someone has to remember to refresh. On top of that, relay_agents_from_directory_events discards the kind 10100 entirely for any agent with a verified kind 30177, which is every properly provisioned agent.

That leaves this PR useful only for agents with no owner-published 30177 — a shrinking set, given v0.5.18 also requires a verified NIP-OA owner.

Happy to close it. I would only argue for keeping it if you want harness-run agents to be discoverable without owner-side provisioning, which is a product question rather than a bug. Your call — say the word and I will close, or I will rebase it if it is still wanted.

(#6098 is unaffected by all this and still stands on its own — it is about the relay never recording agent_owner_pubkey for an agent enrolled as a direct member, which silently disables its NIP-AO observer frames.)

@Schnitzel Schnitzel closed this Aug 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant