mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
Preserve agent p-tags on focused sends
This commit is contained in:
@@ -4,6 +4,7 @@ import test from "node:test";
|
||||
import {
|
||||
canOpenAgentConversationInChannel,
|
||||
getDmTaskAgentPubkeys,
|
||||
getThreadTaskAgentPubkeys,
|
||||
mergeAutoRouteMentionPubkeys,
|
||||
} from "./ChannelPane.helpers.ts";
|
||||
|
||||
@@ -114,3 +115,61 @@ test("DM task agent inference requires exactly one other known agent", () => {
|
||||
[],
|
||||
);
|
||||
});
|
||||
|
||||
test("thread task agent inference requires exactly one known agent and one human", () => {
|
||||
const knownAgentPubkeys = new Set(["agent-one", "agent-two"]);
|
||||
|
||||
assert.deepEqual(
|
||||
getThreadTaskAgentPubkeys({
|
||||
currentPubkey: "human",
|
||||
knownAgentPubkeys,
|
||||
messages: [
|
||||
{
|
||||
pubkey: "human",
|
||||
tags: [["p", "agent-one"]],
|
||||
},
|
||||
{
|
||||
pubkey: "agent-one",
|
||||
tags: [["p", "human"]],
|
||||
},
|
||||
],
|
||||
}),
|
||||
["agent-one"],
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
getThreadTaskAgentPubkeys({
|
||||
currentPubkey: "human",
|
||||
knownAgentPubkeys,
|
||||
messages: [
|
||||
{
|
||||
pubkey: "human",
|
||||
tags: [["p", "agent-one"]],
|
||||
},
|
||||
{
|
||||
pubkey: "other-human",
|
||||
tags: [["p", "human"]],
|
||||
},
|
||||
],
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
getThreadTaskAgentPubkeys({
|
||||
currentPubkey: "human",
|
||||
knownAgentPubkeys,
|
||||
messages: [
|
||||
{
|
||||
pubkey: "human",
|
||||
tags: [["p", "agent-one"]],
|
||||
},
|
||||
{
|
||||
pubkey: "agent-two",
|
||||
tags: [["p", "human"]],
|
||||
},
|
||||
],
|
||||
}),
|
||||
[],
|
||||
);
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { TimelineMessage } from "@/features/messages/types";
|
||||
import type { Channel } from "@/shared/api/types";
|
||||
import { KIND_SYSTEM_MESSAGE } from "@/shared/constants/kinds";
|
||||
import { normalizePubkey } from "@/shared/lib/pubkey";
|
||||
import { getMentionTagPubkey } from "@/shared/lib/resolveMentionNames";
|
||||
|
||||
export function getChannelIntroKind(channel: Channel): string {
|
||||
const isPrivate = channel.visibility === "private";
|
||||
@@ -120,6 +121,53 @@ export function getDmTaskAgentPubkeys({
|
||||
);
|
||||
}
|
||||
|
||||
export function getThreadTaskAgentPubkeys({
|
||||
currentPubkey,
|
||||
knownAgentPubkeys,
|
||||
messages,
|
||||
}: {
|
||||
currentPubkey?: string;
|
||||
knownAgentPubkeys: ReadonlySet<string>;
|
||||
messages: readonly TimelineMessage[];
|
||||
}) {
|
||||
const normalizedCurrentPubkey = currentPubkey
|
||||
? normalizePubkey(currentPubkey)
|
||||
: null;
|
||||
const participants = new Map<string, string>();
|
||||
|
||||
const addParticipant = (pubkey: string | null | undefined) => {
|
||||
if (!pubkey) {
|
||||
return;
|
||||
}
|
||||
const normalized = normalizePubkey(pubkey);
|
||||
if (!normalized || participants.has(normalized)) {
|
||||
return;
|
||||
}
|
||||
participants.set(normalized, pubkey);
|
||||
};
|
||||
|
||||
for (const message of messages) {
|
||||
addParticipant(message.pubkey);
|
||||
for (const tag of message.tags ?? []) {
|
||||
addParticipant(getMentionTagPubkey(tag));
|
||||
}
|
||||
}
|
||||
|
||||
const agentPubkeys = new Map<string, string>();
|
||||
|
||||
for (const [normalized, pubkey] of participants) {
|
||||
if (normalizedCurrentPubkey && normalized === normalizedCurrentPubkey) {
|
||||
continue;
|
||||
}
|
||||
if (!knownAgentPubkeys.has(normalized)) {
|
||||
return [];
|
||||
}
|
||||
agentPubkeys.set(normalized, pubkey);
|
||||
}
|
||||
|
||||
return agentPubkeys.size === 1 ? [...agentPubkeys.values()] : [];
|
||||
}
|
||||
|
||||
export function mergeAutoRouteMentionPubkeys({
|
||||
autoRouteAgentPubkeys,
|
||||
mentionPubkeys,
|
||||
@@ -148,3 +196,20 @@ export function mergeAutoRouteMentionPubkeys({
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
export function mergeTaskAgentMentionPubkeys({
|
||||
agentPubkeys,
|
||||
mentionPubkeys,
|
||||
}: {
|
||||
agentPubkeys: readonly string[];
|
||||
mentionPubkeys: string[];
|
||||
}) {
|
||||
if (agentPubkeys.length === 0) {
|
||||
return mentionPubkeys;
|
||||
}
|
||||
|
||||
return mergeAutoRouteMentionPubkeys({
|
||||
autoRouteAgentPubkeys: agentPubkeys,
|
||||
mentionPubkeys,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -44,7 +44,9 @@ import {
|
||||
getChannelIntroDescription,
|
||||
getChannelIntroKind,
|
||||
getDmTaskAgentPubkeys,
|
||||
getThreadTaskAgentPubkeys,
|
||||
isWelcomeSetupSystemMessage,
|
||||
mergeTaskAgentMentionPubkeys,
|
||||
mentionsKnownAgent,
|
||||
} from "@/features/channels/ui/ChannelPane.helpers";
|
||||
import type { ChannelPaneProps } from "@/features/channels/ui/ChannelPane.types";
|
||||
@@ -421,13 +423,17 @@ export const ChannelPane = React.memo(function ChannelPane({
|
||||
mentionPubkeys: string[],
|
||||
mediaTags?: string[][],
|
||||
) => {
|
||||
const sendMentionPubkeys = mergeTaskAgentMentionPubkeys({
|
||||
agentPubkeys: dmTaskAgentPubkeys,
|
||||
mentionPubkeys,
|
||||
});
|
||||
const shouldCompleteWelcomeBanner =
|
||||
isActiveWelcomeChannel &&
|
||||
(containsWelcomePersonaMention(content) ||
|
||||
mentionsKnownAgent(mentionPubkeys, knownAgentPubkeys));
|
||||
mentionsKnownAgent(sendMentionPubkeys, knownAgentPubkeys));
|
||||
|
||||
messageTimelineRef.current?.scrollToBottomOnNextUpdate();
|
||||
await onSendMessage(content, mentionPubkeys, mediaTags);
|
||||
await onSendMessage(content, sendMentionPubkeys, mediaTags);
|
||||
|
||||
if (shouldCompleteWelcomeBanner) {
|
||||
completeWelcomeComposerBanner();
|
||||
@@ -435,6 +441,7 @@ export const ChannelPane = React.memo(function ChannelPane({
|
||||
},
|
||||
[
|
||||
completeWelcomeComposerBanner,
|
||||
dmTaskAgentPubkeys,
|
||||
isActiveWelcomeChannel,
|
||||
knownAgentPubkeys,
|
||||
onSendMessage,
|
||||
@@ -764,6 +771,22 @@ export const ChannelPane = React.memo(function ChannelPane({
|
||||
...threadMessages.map((entry) => entry.message),
|
||||
];
|
||||
}, [threadHeadMessage, threadMessages]);
|
||||
const threadTaskAgentPubkeys = getThreadTaskAgentPubkeys({
|
||||
currentPubkey,
|
||||
knownAgentPubkeys,
|
||||
messages: threadSourceMessages,
|
||||
});
|
||||
const handleSendThreadReply = React.useCallback(
|
||||
(content: string, mentionPubkeys: string[], mediaTags?: string[][]) => {
|
||||
const sendMentionPubkeys = mergeTaskAgentMentionPubkeys({
|
||||
agentPubkeys: threadTaskAgentPubkeys,
|
||||
mentionPubkeys,
|
||||
});
|
||||
|
||||
return onSendThreadReply(content, sendMentionPubkeys, mediaTags);
|
||||
},
|
||||
[onSendThreadReply, threadTaskAgentPubkeys],
|
||||
);
|
||||
const hiddenAgentConversationMessageIds = React.useMemo(() => {
|
||||
if (!enableAgentConversations) {
|
||||
return new Set<string>();
|
||||
@@ -1194,7 +1217,7 @@ export const ChannelPane = React.memo(function ChannelPane({
|
||||
: undefined
|
||||
}
|
||||
onSelectReplyTarget={onSelectThreadReplyTarget}
|
||||
onSend={onSendThreadReply}
|
||||
onSend={handleSendThreadReply}
|
||||
onScrollTargetResolved={onThreadScrollTargetResolved}
|
||||
onToggleReaction={onToggleReaction}
|
||||
onUnfollowThread={onUnfollowThread}
|
||||
|
||||
Reference in New Issue
Block a user