fix(acp,core,relay): publish agent profiles on kind:10100 and move channel-add policy to 10101 - #4994
Open
jongouveia wants to merge 3 commits into
Open
fix(acp,core,relay): publish agent profiles on kind:10100 and move channel-add policy to 10101#4994jongouveia wants to merge 3 commits into
jongouveia wants to merge 3 commits into
Conversation
Clients build their agent directory from kind:10100 agent-profile events, but nothing published them. The desktop app publishes only a kind:0 profile for a managed agent, and channel-level bot membership (kind:39002) is not a source those directories read, so a running agent never appeared in the list and could not be tracked for presence. buzz-acp now publishes a signed kind:10100 profile over the authenticated POST /events bridge once channel discovery finishes, and republishes it when membership changes so channel_ids stays current. Content carries the name, respond_to mode, and allowlist that consumers use for mention eligibility. The name comes from the agent's own kind:0 display name, falling back to the normalized runtime command when no profile exists. Publishing is gated on the existing presence flag, and a failure only warns. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DUks9koR8d9L3gC9ARsSLy Signed-off-by: Jon Gouveia <jon@pixelcove.co>
Kind 10100 was doing two jobs. The registry documents it as the agent profile, and the desktop and mobile clients both read it that way, but the relay also used it to carry a user's channel_add_policy and rejected any 10100 without that field. Since 10100 is replaceable, an agent profile and a policy silently overwrote each other. Adds KIND_CHANNEL_ADD_POLICY (10101) for the policy and leaves agent profiles on 10100. The relay accepts the new kind with the same UsersWrite scope and routes it to the policy handler, renamed to say what it does. Older clients still work: a 10100 carrying channel_add_policy is honored, and one without it is now a no-op instead of an error. The CLI publishes the new kind. No migration. The enforced policy lives in the users table and is written by the side effect, so existing rows stay valid. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DUks9koR8d9L3gC9ARsSLy Signed-off-by: Jon Gouveia <jon@pixelcove.co>
Signed-off-by: Jon Gouveia <jon@pixelcove.co>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Kind 10100 is doing two incompatible jobs, and because it is replaceable (one event per author) the two overwrite each other.
As an agent profile.
crates/buzz-core/src/kind.rsdocumentsKIND_AGENT_PROFILE = 10100as "Agent metadata + owner reference (replaceable, agent-authored)", and three consumers read it that way:desktop/src-tauri/src/nostr_convert.rs—agents_from_eventsdesktop/src-tauri/src/commands/agent_discovery.rs— querieskinds: [10100]nostr_filters.dart(agentProfiles()),agent_identity_provider.dart(AgentDirectoryEntry.fromEvent)As a channel-add policy.
buzz channels set-add-policypublishes kind:10100 with{"channel_add_policy": ...}, and the relay's side effect requires that field, rejecting anything without it:Nothing ever published the agent profile side, so the conflict stayed dormant. The consequence is visible on mobile: a running agent never appears in the directory, and because the presence cache only tracks pubkeys the app has discovered, it also never shows as online.
Changes
1.
buzz-acppublishes the agent profile (kind:10100).Published over the authenticated
POST /eventsbridge once channel discovery completes, and republished when membership changes sochannel_idsstays current. Content carries the fields consumers already parse:name,display_name,agent_type,channels,channel_ids,capabilities,status,respond_to,respond_to_allowlist.The name is resolved once at startup from the agent's own kind:0
display_name, falling back to the normalized runtime command when no kind:0 profile exists. Without that, every agent would advertise its runtime ("codex-acp", "claude-agent-acp") instead of its name. Publishing is gated on the existing presence flag and a failure only warns.2. The channel-add policy moves to its own kind (10101).
KIND_CHANNEL_ADD_POLICY = 10101is registered inALL_KINDSwith the usual replaceable compile-time assertion. The relay accepts it under the sameUsersWritescope and routes it to the policy handler, renamed fromhandle_agent_profiletohandle_channel_add_policysince that is all it ever did. The CLI publishes the new kind.Back-compat runs both ways: a kind:10100 that still carries
channel_add_policyis honored for older clients, and one without it is a no-op instead of an error.No migration. The enforced policy lives in the users table and is written by the side effect, so existing rows stay valid.
The desktop and mobile clients need no changes — they already read 10100 as the agent profile.
Testing
just cipasses on this branch merged withmain(exit 0).New coverage:
agent_profile_event_contains_mobile_directory_fields— event shape, kind, author, sorted channel and allowlist arraysagent_profile_event_falls_back_to_runtime_identity_without_kind0_profile— name fallbackchannel_add_policy_kind_is_registered_and_replaceablechannel_add_policy_is_global_only_and_requires_users_writelegacy_agent_profile_channel_add_policy_still_applies_policy(Postgres-gated)agent_profile_without_channel_add_policy_is_a_no_op(Postgres-gated)The two Postgres-gated tests were not exercised locally; they need
just testwith Postgres and Redis.Manual check: running the built harness against a live relay logs
agent profile publishedafterdiscovered N channel(s), and the relay accepts the event.