Skip to content
Merged
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
91 changes: 80 additions & 11 deletions docs/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,10 @@ not complete: automatic-recipient inference must honor `complete`, and automatic
saved-template resolution must wait for required pending identity/roster evidence.

Action policy stays explicit: ordinary member mentions use the channel roster and
never acquire template archive gates. Ordinary nonmember enrollment admits managed
same-community identities; session invitations also allow existing legacy choices.
hide known-archived identities without requiring verified non-archived evidence.
Ordinary nonmember mentions also offer people from the selected community directory
and eligible managed agents. Send asks before adding them; selection grants no
access. Session invitations retain their existing rules, including legacy choices.
Templates additionally require verified non-archived state, and legacy-only choices
need visible community membership. Save-as-template discloses an incomplete inferred
lineup when either inventory or roster evidence is partial; it never claims a full
Expand All @@ -119,13 +121,75 @@ thread composers through the shared conversation tool contract. The host retains
recipient intent, inline editing and avatar removal even when the chooser plugin is disabled.
The picker shows keys alongside names (namesakes remain separate), reads optional
profiles only on demand, and keeps selected identity spans in scoped drafts.
Typing a name alone does not notify anyone. Editing a selected span removes its
notification intent. Native beforeinput ranges preserve untouched spans; missing
Typing a name alone does not notify anyone. Plain Space after a unique exact name
selects its recipient; ambiguous names require Tab, Enter or click. Editing a
selected span removes its notification intent. Native beforeinput ranges preserve untouched spans; missing
range evidence, IME/history edits and collapsed deletions clear selections rather
than guess. Even a same-text replacement drops the edited identity. Selected mentions appear as inline identity chips in the composer. Namesakes
selected together receive visible key qualifiers; editing a selected span removes
its notification intent. Chips remain available without the Mentions chooser.

### Chooser rules

Both the toolbar picker and inline completion use `mention-candidates.ts` and
`mention-ranking.ts`. Membership permits notification, not a promise that an agent
will accept or answer the prompt. DMs do not gain outside recipients. Ordinary
nonmember consent and session invitation rules remain the access owners;
selection itself neither grants access nor starts an agent. Invalid recipient
keys, known-archived identities, and archived/read-only destinations are excluded.
The viewer is never hidden from themself. Unknown archive state does not block
selection. Optional archive reads are lazy.

Search trims and lowercases the query. Members precede nonmembers, with humans and agents in each group. Within each
group, matches against the visible resolved label come first: whole-name exact,
name prefix, whole-word exact, then word prefix. Base names and known aliases are
fallback matches in that same order. Only resolved names and real profile/agent
names are searchable. Public keys (including unnamed identity fallbacks) are not
completion matches. Arbitrary name substrings do not match. A hidden base-name match never
promotes a weaker visible-label match. When visible-label match quality ties,
base-name/alias match quality breaks the tie before recipient preferences.
Among equal matches, agents with profile-reported ownership by the viewer come
first, before humans and other agents. Agents that share a base name then form one
block, placed at the first of their case-insensitive displayed labels (including
disambiguating suffixes). Inside a block, explicit-choice recency, managed status,
and already-known online/away status decide the order. Humans never join a block
and sort first on an equal label. Remaining ties use the displayed label, then the
full key. Each rule is a per-choice sort key, so the order is the same for any
input order. Ownership comes
from profile owner metadata, not a name or presence in the saved library.
Recency stays in memory per session/destination and is bounded to 100 destinations
and 100 recipients each. Neither ownership nor recency overrides membership or
match quality.

An open query installs at most 50 keys. Their order and membership stay fixed until
the query changes or the chooser reopens. Labels, insertion names and availability
remain live. A removed/archived row stays disabled in place; Enter/Tab cannot fall
through to sending. A member who becomes an outside invitation choice also stays
disabled until reopening. Retry refreshes evidence, not the installed order. New
arrivals need a changed query or reopening. Pending sources or missing profiles do
not freeze a premature empty result. Local sources (members and agent choices)
establish the list; the community directory never gates it. Directory people
append below the rows already shown, so a late page never moves a visible row.
While a new query waits or loads, still-matching people from the last settled
page of the same chooser stay visible (one picker, or one inline `@` token; inline
completion remounts per keystroke, so the page is kept per session outside it) and the chooser shows "Searching community…". Uncached queries
reach the network only after a 200 ms typing pause. Settled first pages are cached
per session and query (100 queries); errors are not cached, and Retry reads the
current query again. Identity naming uses eligible candidates plus the
current draft recipients, not every cached profile.

Plain Space selects only a unique exact name/alias/label across the full uncapped
candidate set, and only if that identity is displayed and still eligible. A known
longer name beginning with that name plus a space prevents selection. Partial
names, ambiguous names, modified Space, IME composition, code and protected literal
ranges keep ordinary editing behavior. Selection rechecks available evidence and
stores only `{pubkey, name}`; qualifiers are presentation, not wire data.

The composer rejects already-known archived recipients (never the viewer) at send entry and omits
ineligible agents from the next draft. This is not an archive transaction: archive
changes during enrollment, dispatch or retry are intentionally not covered. The
existing relay membership/send/retry validator is unchanged.

After an accepted send, the next draft starts with the exact selected agent-name
mentions, deduplicated by key. Agent classification uses already-cached profile hints
or the shared agent-choice projection (legacy and native), not a new lookup or
Expand All @@ -140,12 +204,16 @@ of relay delivery or agent execution.

The picker supports Up/Down navigation, Enter selection and Escape dismissal.

`session.messages.send/reply` accepts up to 32 exact pubkeys and emits deduplicated
`p` tags. Selection never invites someone. The native local-agent flow now offers
same-community managed agents too: the composer enrolls a selected nonmember on
Send, verifies the roster, then calls this unchanged message API. See
[local agent controls](agent-control.md#normal-desktop-workflow). Ordinary nonmember
people are not automatically added. Current roster membership is checked at
`session.messages.send/reply` accepts up to 32 exact notification pubkeys and emits
deduplicated `p` tags. In ordinary channels, Send pauses for selected nonmembers:
**Invite** grants access only with permission and explicit consent, waits
for confirmed membership, then sends notifications. **Do nothing** (or **Send
anyway** without add permission) sends those identities as separate `mention`
reference tags, without adding or notifying them. Close or Escape keeps the draft. Existing member mentions still notify in a
mixed send. Reference keys are validated and bounded to 32. Selection itself never
invites or starts anyone; confirmed outgoing notifications own agent wakeup. See
[local agent controls](agent-control.md#normal-desktop-workflow). DM and session
admission paths remain separate. Current notification-recipient membership is checked at
intent, before signing, and after signing before entering the transport publisher;
retry/restored signed intent uses the same publisher check. Before **each**
mention publication the session performs a bounded foreground finite read of this
Expand All @@ -168,7 +236,8 @@ not a substitute for relay authorization, a membership transaction, or the ACP
listener's own admission rules. Network changes after transport dispatch remain
possible. No ownership or running status is inferred from a member's name/profile.

Wire compatibility is kind 9 + `h` + exact `p`; direct replies also carry
Wire compatibility is kind 9 + `h` + exact `p` for notifications and `mention`
for reference-only identities; direct replies also carry
`["e", root, "", "reply"]`. Existing buzz-acp owns mention admission, replay,
channel membership, pool wake and harness execution. This slice adds no wake loop,
process launcher, configuration save or agent invitation operation. The local library and archive display are not mention authorization.
Expand Down
4 changes: 4 additions & 0 deletions src/bundled/composer/lab/fixture.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ const rawSession = {
archives: {
snapshot: () => archiveSnapshot,
subscribe: empty,
// Archive reads are unavailable here, as the real contract reports them.
state: () => "unknown" as const,
ensure: async () => {},
refresh: async () => {},
},
emoji: {
snapshot: () => emojiSnapshot,
Expand Down
2 changes: 1 addition & 1 deletion src/bundled/identity-naming/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ different case-folding rule is a different policy, not byte-identical conformanc
[policy.test.ts](../../features/identity-names/policy.test.ts).
- Context and fact selection: [directory.ts](../../features/identity-names/directory.ts),
[react.ts](../../features/identity-names/react.ts), and
[mention-choices.ts](../mentions/mention-choices.ts).
[use-mention-choices.ts](../mentions/use-mention-choices.ts).
- buzz-app feature-contract precedent: [Profiles](../../../docs/profiles.md) and
[Unread](../../../docs/unread.md), especially explicit consumer and authority boundaries.
- block/buzz, inspected at `797012ff01a6d499959b45ed2e56f7927c6a4d6b`:
Expand Down
Loading
Loading