mirror of
https://github.com/block/buzz.git
synced 2026-10-27 04:30:52 +00:00
Preserve managed agent mentions during relay errors (#6167)
## Summary - preserve selected managed-agent `p` tags when fresh managed-directory evidence succeeds but relay discovery or owner-profile lookup fails - keep relay-only agents fail-closed unless fresh relay evidence and any required owner proof are available - cover selective admission with focused unit tests and a signed-event Playwright regression ## Testing - `node --import ./desktop/test-loader.mjs --experimental-strip-types --test desktop/src/features/messages/lib/agentMentionRevalidation.test.mjs` (7 passed) - focused Playwright regression plus adjacent relay-revocation case (2 passed) - pre-commit desktop Biome/file-size hook - pre-push desktop check, TypeScript typecheck, and full desktop unit suite (4,987 passed) Fixes #6147 Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
This commit is contained in:
@@ -7,6 +7,7 @@ const CURRENT = "a".repeat(64);
|
||||
const AGENT = "b".repeat(64);
|
||||
const HUMAN = "c".repeat(64);
|
||||
const OTHER_OWNER = "d".repeat(64);
|
||||
const LOCAL_AGENT = "e".repeat(64);
|
||||
|
||||
function options(refetchOwnerProfiles) {
|
||||
return {
|
||||
@@ -49,6 +50,62 @@ test("owner-only revalidation admits an agent only from a fresh same-owner proof
|
||||
assert.deepEqual(result, [HUMAN, AGENT]);
|
||||
});
|
||||
|
||||
test("fresh managed evidence survives unrelated relay authorization errors", async () => {
|
||||
const result = await revalidateAgentMentionPubkeys({
|
||||
...options(async () => {
|
||||
throw new Error("owner profiles unavailable");
|
||||
}),
|
||||
pubkeys: [HUMAN, LOCAL_AGENT],
|
||||
agentPubkeys: new Set([LOCAL_AGENT]),
|
||||
refetchManagedAgents: async () => ({
|
||||
data: [{ pubkey: LOCAL_AGENT }],
|
||||
error: null,
|
||||
}),
|
||||
refetchRelayAgents: async () => ({
|
||||
data: undefined,
|
||||
error: new Error("relay directory unavailable"),
|
||||
}),
|
||||
});
|
||||
|
||||
assert.deepEqual(result, [HUMAN, LOCAL_AGENT]);
|
||||
});
|
||||
|
||||
test("relay-only agents still fail closed when relay discovery fails", async () => {
|
||||
const result = await revalidateAgentMentionPubkeys({
|
||||
...options(async () => ({
|
||||
profiles: { [AGENT]: { ownerPubkey: CURRENT } },
|
||||
missing: [],
|
||||
})),
|
||||
refetchRelayAgents: async () => ({
|
||||
data: undefined,
|
||||
error: new Error("relay directory unavailable"),
|
||||
}),
|
||||
});
|
||||
|
||||
assert.deepEqual(result, [HUMAN]);
|
||||
});
|
||||
|
||||
test("mixed evidence preserves only fresh managed agents and humans", async () => {
|
||||
const result = await revalidateAgentMentionPubkeys({
|
||||
...options(async () => ({
|
||||
profiles: { [AGENT]: { ownerPubkey: CURRENT } },
|
||||
missing: [LOCAL_AGENT],
|
||||
})),
|
||||
pubkeys: [HUMAN, LOCAL_AGENT, AGENT],
|
||||
agentPubkeys: new Set([LOCAL_AGENT, AGENT]),
|
||||
refetchManagedAgents: async () => ({
|
||||
data: [{ pubkey: LOCAL_AGENT }],
|
||||
error: null,
|
||||
}),
|
||||
refetchRelayAgents: async () => ({
|
||||
data: undefined,
|
||||
error: new Error("relay directory unavailable"),
|
||||
}),
|
||||
});
|
||||
|
||||
assert.deepEqual(result, [HUMAN, LOCAL_AGENT]);
|
||||
});
|
||||
|
||||
for (const [name, refetchOwnerProfiles] of [
|
||||
["revoked owner proof", async () => ({ profiles: {}, missing: [AGENT] })],
|
||||
[
|
||||
|
||||
@@ -57,14 +57,13 @@ export async function revalidateAgentMentionPubkeys({
|
||||
? refetchOwnerProfiles([...requestedAgentPubkeys]).catch(() => null)
|
||||
: Promise.resolve(null),
|
||||
]);
|
||||
const relayDirectoryReady =
|
||||
relayResult.error === null && relayResult.data !== undefined;
|
||||
if (
|
||||
managedResult.error !== null ||
|
||||
relayResult.error !== null ||
|
||||
managedResult.data === undefined ||
|
||||
relayResult.data === undefined ||
|
||||
ownerOnly === undefined ||
|
||||
ownerPolicyError !== null ||
|
||||
(ownerOnly && ownerProfiles === null)
|
||||
managedResult.error !== null ||
|
||||
managedResult.data === undefined
|
||||
) {
|
||||
return filterAdmittedMentionPubkeys(pubkeys, agentPubkeys, new Set());
|
||||
}
|
||||
@@ -76,23 +75,28 @@ export async function revalidateAgentMentionPubkeys({
|
||||
currentPubkey,
|
||||
eligibilityScope,
|
||||
managedAgentPubkeys: managedPubkeys,
|
||||
relayAgents: relayResult.data,
|
||||
relayAgents: relayDirectoryReady ? relayResult.data : [],
|
||||
sharedChannelIds,
|
||||
});
|
||||
const admittedPubkeys = new Set(
|
||||
[...agentPubkeys].filter(
|
||||
(pubkey) =>
|
||||
[...agentPubkeys].filter((pubkey) => {
|
||||
const isManagedAgent = managedPubkeys.has(normalizePubkey(pubkey));
|
||||
const directoryReady =
|
||||
isManagedAgent ||
|
||||
(relayDirectoryReady && (!ownerOnly || ownerProfiles !== null));
|
||||
return (
|
||||
getAgentMentionAdmission({
|
||||
isAgent: true,
|
||||
isManagedAgent: managedPubkeys.has(pubkey),
|
||||
isManagedAgent,
|
||||
pubkey,
|
||||
ownerPubkey: ownerProfiles?.profiles[pubkey]?.ownerPubkey,
|
||||
currentPubkey,
|
||||
mentionableAgentPubkeys: mentionablePubkeys,
|
||||
directoryReady: true,
|
||||
directoryReady,
|
||||
ownerOnly,
|
||||
}) === "allow",
|
||||
),
|
||||
}) === "allow"
|
||||
);
|
||||
}),
|
||||
);
|
||||
return filterAdmittedMentionPubkeys(pubkeys, agentPubkeys, admittedPubkeys);
|
||||
}
|
||||
|
||||
@@ -1214,6 +1214,52 @@ test("relay-only allowlisted agents emit a p tag when sent", async ({
|
||||
.toContain(ALLOWLIST_RELAY_AGENT_PUBKEY);
|
||||
});
|
||||
|
||||
test("managed agents keep their p tag when relay discovery fails before send", async ({
|
||||
page,
|
||||
}) => {
|
||||
await installMockBridge(page, {
|
||||
managedAgents: [
|
||||
{
|
||||
pubkey: ALLOWLIST_RELAY_AGENT_PUBKEY,
|
||||
name: "quinn",
|
||||
status: "running",
|
||||
},
|
||||
],
|
||||
relayAgents: [
|
||||
{
|
||||
pubkey: ALLOWLIST_RELAY_AGENT_PUBKEY,
|
||||
name: "quinn",
|
||||
respondTo: "allowlist",
|
||||
respondToAllowlist: [MOCK_VIEWER_PUBKEY],
|
||||
channelNames: ["general"],
|
||||
},
|
||||
],
|
||||
});
|
||||
await page.goto("/");
|
||||
await page.getByTestId("channel-general").click();
|
||||
|
||||
const input = page.getByTestId("message-input");
|
||||
await input.fill("@quinn");
|
||||
const quinnRow = autocomplete(page).locator("button", { hasText: "quinn" });
|
||||
await expect(quinnRow).toBeVisible();
|
||||
await quinnRow.click();
|
||||
await expect(input).toHaveText("@quinn ");
|
||||
await page.keyboard.type("hello");
|
||||
await expect(input).toHaveText("@quinn hello");
|
||||
|
||||
await page.evaluate(() => {
|
||||
window.__BUZZ_E2E__.mock ??= {};
|
||||
window.__BUZZ_E2E__.mock.relayAgentListErrors = Array(5).fill(
|
||||
"mock unrelated relay directory failure",
|
||||
);
|
||||
});
|
||||
await page.getByTestId("send-message").click();
|
||||
|
||||
await expect
|
||||
.poll(() => readOutgoingMentionPubkeys(page, "@quinn hello"))
|
||||
.toContain(ALLOWLIST_RELAY_AGENT_PUBKEY);
|
||||
});
|
||||
|
||||
test("selected relay agents revoked before send emit no p tag", async ({
|
||||
page,
|
||||
}) => {
|
||||
|
||||
Reference in New Issue
Block a user