Skip to content

Commit 8ae4e57

Browse files
wesbillmanPinky
andcommitted
fix(desktop): include allowlisted relay agents in autocomplete
Carry relay-agent respond-to allowlist metadata through the directory model and treat allowlisted-to-current-user agents as reachable autocomplete candidates. Co-authored-by: Pinky <44b8e82baa6e0e254e0208d68f335c283c94e7b78dd1fa10d5a49d3f13dd0435@sprout-oss.stage.blox.sqprod.co> Signed-off-by: Wes <wesbillman@users.noreply.github.com>
1 parent f24d539 commit 8ae4e57

13 files changed

Lines changed: 229 additions & 20 deletions

File tree

desktop/src-tauri/src/managed_agents/types.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
use std::{collections::BTreeMap, path::PathBuf, process::Child};
2-
31
use serde::{Deserialize, Serialize};
2+
use std::{collections::BTreeMap, path::PathBuf, process::Child};
43

54
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
65
#[serde(tag = "type", rename_all = "snake_case")]
@@ -80,8 +79,9 @@ pub struct RelayAgentInfo {
8079
pub status: String,
8180
#[serde(default)]
8281
pub respond_to: Option<RespondTo>,
82+
#[serde(default)]
83+
pub respond_to_allowlist: Vec<String>,
8384
}
84-
8585
#[derive(Debug, Clone, Serialize, Deserialize)]
8686
pub struct ManagedAgentRecord {
8787
pub pubkey: String,

desktop/src-tauri/src/nostr_convert.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -940,6 +940,26 @@ mod tests {
940940
);
941941
}
942942

943+
#[test]
944+
fn agents_preserves_allowlist_metadata_for_directory_parse() {
945+
let e = ev(
946+
10100,
947+
r#"{"name":"Scout","respond_to":"allowlist","respond_to_allowlist":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]}"#,
948+
vec![],
949+
);
950+
let v = agents_from_events(std::slice::from_ref(&e));
951+
let agents = v.get("agents").cloned().unwrap();
952+
let parsed: Vec<crate::managed_agents::RelayAgentInfo> =
953+
serde_json::from_value(agents).unwrap();
954+
955+
assert_eq!(parsed.len(), 1);
956+
assert_eq!(
957+
parsed[0].respond_to,
958+
Some(crate::managed_agents::RespondTo::Allowlist)
959+
);
960+
assert_eq!(parsed[0].respond_to_allowlist, vec!["a".repeat(64)]);
961+
}
962+
943963
#[test]
944964
fn relay_members_dedupes_and_defaults_role() {
945965
let pk1 = "a".repeat(64);

desktop/src/features/agents/lib/agentAutocompleteEligibility.test.mjs

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,44 +45,93 @@ test("getSharedChannelIds: includes only active joined channels", () => {
4545
);
4646
});
4747

48-
test("relayAgentIsSharedWithUser: requires anyone response and a shared channel", () => {
48+
test("relayAgentIsSharedWithUser: accepts shared anyone agents and rejects unshared ones", () => {
4949
const sharedChannelIds = new Set(["general"]);
5050

5151
assert.equal(
5252
relayAgentIsSharedWithUser(
53-
{ respondTo: "anyone", channelIds: ["general"] },
53+
{ respondTo: "anyone", respondToAllowlist: [], channelIds: ["general"] },
5454
sharedChannelIds,
5555
),
5656
true,
5757
);
5858
assert.equal(
5959
relayAgentIsSharedWithUser(
60-
{ respondTo: "owner-only", channelIds: ["general"] },
60+
{
61+
respondTo: "owner-only",
62+
respondToAllowlist: [],
63+
channelIds: ["general"],
64+
},
6165
sharedChannelIds,
6266
),
6367
false,
6468
);
6569
assert.equal(
6670
relayAgentIsSharedWithUser(
67-
{ respondTo: "anyone", channelIds: ["other"] },
71+
{ respondTo: "anyone", respondToAllowlist: [], channelIds: ["other"] },
6872
sharedChannelIds,
6973
),
7074
false,
7175
);
7276
});
7377

78+
test("relayAgentIsSharedWithUser: accepts allowlist agents for the current user", () => {
79+
const sharedChannelIds = new Set(["general"]);
80+
81+
assert.equal(
82+
relayAgentIsSharedWithUser(
83+
{
84+
respondTo: "allowlist",
85+
respondToAllowlist: [OTHER_OWNER_PUBKEY, CURRENT_PUBKEY.toUpperCase()],
86+
channelIds: ["other"],
87+
},
88+
sharedChannelIds,
89+
CURRENT_PUBKEY,
90+
),
91+
true,
92+
);
93+
assert.equal(
94+
relayAgentIsSharedWithUser(
95+
{
96+
respondTo: "allowlist",
97+
respondToAllowlist: [OTHER_OWNER_PUBKEY],
98+
channelIds: ["general"],
99+
},
100+
sharedChannelIds,
101+
CURRENT_PUBKEY,
102+
),
103+
false,
104+
);
105+
});
106+
74107
test("getMentionableAgentPubkeys: keeps managed agents and shared relay agents", () => {
75108
const result = getMentionableAgentPubkeys({
76109
managedAgentPubkeys: [PUB_A],
110+
currentPubkey: CURRENT_PUBKEY,
77111
relayAgents: [
78-
{ pubkey: PUB_B, respondTo: "anyone", channelIds: ["general"] },
79-
{ pubkey: PUB_C, respondTo: "owner-only", channelIds: ["general"] },
80-
{ pubkey: PUB_D, respondTo: "anyone", channelIds: ["other"] },
112+
{
113+
pubkey: PUB_B,
114+
respondTo: "anyone",
115+
respondToAllowlist: [],
116+
channelIds: ["general"],
117+
},
118+
{
119+
pubkey: PUB_C,
120+
respondTo: "allowlist",
121+
respondToAllowlist: [CURRENT_PUBKEY],
122+
channelIds: ["other"],
123+
},
124+
{
125+
pubkey: PUB_D,
126+
respondTo: "anyone",
127+
respondToAllowlist: [],
128+
channelIds: ["other"],
129+
},
81130
],
82131
sharedChannelIds: new Set(["general"]),
83132
});
84133

85-
assert.deepEqual(result, new Set([PUB_A, PUB_B]));
134+
assert.deepEqual(result, new Set([PUB_A, PUB_B, PUB_C]));
86135
});
87136

88137
test("coalesceAgentAutocompleteCandidates: merges agents with the same persona id", () => {

desktop/src/features/agents/lib/agentAutocompleteEligibility.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,33 @@ export function getSharedChannelIds(channels: readonly Channel[] | undefined) {
1010
}
1111

1212
export function relayAgentIsSharedWithUser(
13-
agent: Pick<RelayAgent, "channelIds" | "respondTo">,
13+
agent: Pick<RelayAgent, "channelIds" | "respondTo" | "respondToAllowlist">,
1414
sharedChannelIds: ReadonlySet<string>,
15+
currentPubkey?: string | null,
1516
) {
17+
const normalizedCurrentPubkey = currentPubkey
18+
? normalizePubkey(currentPubkey)
19+
: null;
20+
21+
if (agent.respondTo === "allowlist" && normalizedCurrentPubkey) {
22+
return agent.respondToAllowlist
23+
.map((pubkey) => normalizePubkey(pubkey))
24+
.includes(normalizedCurrentPubkey);
25+
}
26+
1627
return (
17-
// RelayAgent does not expose respondTo allowlists yet, so only shared "anyone" agents are safely mentionable.
1828
agent.respondTo === "anyone" &&
1929
agent.channelIds.some((channelId) => sharedChannelIds.has(channelId))
2030
);
2131
}
2232

2333
export function getMentionableAgentPubkeys({
34+
currentPubkey,
2435
managedAgentPubkeys,
2536
relayAgents,
2637
sharedChannelIds,
2738
}: {
39+
currentPubkey?: string | null;
2840
managedAgentPubkeys: Iterable<string>;
2941
relayAgents: readonly RelayAgent[] | undefined;
3042
sharedChannelIds: ReadonlySet<string>;
@@ -34,7 +46,7 @@ export function getMentionableAgentPubkeys({
3446
);
3547

3648
for (const agent of relayAgents ?? []) {
37-
if (relayAgentIsSharedWithUser(agent, sharedChannelIds)) {
49+
if (relayAgentIsSharedWithUser(agent, sharedChannelIds, currentPubkey)) {
3850
pubkeys.add(normalizePubkey(agent.pubkey));
3951
}
4052
}

desktop/src/features/channels/ui/MembersSidebar.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ export function MembersSidebar({
286286
);
287287
const sharedChannelIds = getSharedChannelIds(channelsQuery.data);
288288
const eligibleAgentPubkeys = getMentionableAgentPubkeys({
289+
currentPubkey,
289290
managedAgentPubkeys: (managedAgentsQuery.data ?? []).map(
290291
(agent) => agent.pubkey,
291292
),

desktop/src/features/messages/lib/useMentions.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,11 +227,17 @@ export function useMentions(
227227
const mentionableAgentPubkeys = React.useMemo(
228228
() =>
229229
getMentionableAgentPubkeys({
230+
currentPubkey,
230231
managedAgentPubkeys,
231232
relayAgents: relayAgentsQuery.data,
232233
sharedChannelIds,
233234
}),
234-
[managedAgentPubkeys, relayAgentsQuery.data, sharedChannelIds],
235+
[
236+
currentPubkey,
237+
managedAgentPubkeys,
238+
relayAgentsQuery.data,
239+
sharedChannelIds,
240+
],
235241
);
236242
const personaNameByPubkey = React.useMemo(() => {
237243
const agents = managedAgentsQuery.data ?? [];

desktop/src/features/pulse/ui/PulseView.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ export function PulseView({ currentPubkey }: PulseViewProps) {
111111
? "online"
112112
: "offline",
113113
respondTo: agent.respondTo,
114+
respondToAllowlist: agent.respondToAllowlist,
114115
});
115116
}
116117
}

desktop/src/features/sidebar/ui/NewDirectMessageDialog.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ export function NewDirectMessageDialog({
337337
? normalizePubkey(currentPubkey)
338338
: null;
339339
const eligibleAgentPubkeys = getMentionableAgentPubkeys({
340+
currentPubkey,
340341
managedAgentPubkeys: (managedAgentsQuery.data ?? []).map(
341342
(agent) => agent.pubkey,
342343
),

desktop/src/shared/api/tauri.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { invoke as tauriInvoke } from "@tauri-apps/api/core";
2-
32
import type {
43
AddChannelMembersInput,
54
AddChannelMembersResult,
@@ -189,8 +188,8 @@ type RawRelayAgent = {
189188
capabilities: string[];
190189
status: RelayAgent["status"];
191190
respond_to?: RelayAgent["respondTo"];
191+
respond_to_allowlist?: string[];
192192
};
193-
194193
export type RawManagedAgent = {
195194
pubkey: string;
196195
name: string;
@@ -846,6 +845,7 @@ function fromRawRelayAgent(agent: RawRelayAgent): RelayAgent {
846845
capabilities: agent.capabilities,
847846
status: agent.status,
848847
respondTo: agent.respond_to ?? null,
848+
respondToAllowlist: agent.respond_to_allowlist ?? [],
849849
};
850850
}
851851

desktop/src/shared/api/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ export type RelayAgent = {
265265
capabilities: string[];
266266
status: "online" | "away" | "offline";
267267
respondTo: RespondToMode | null;
268+
respondToAllowlist: string[];
268269
};
269270

270271
export type ManagedAgentBackend =

0 commit comments

Comments
 (0)