+
0 ? (
{
});
});
+describe("DM agent typing classification", () => {
+ it("admits a known relay agent who participates in the active DM", () => {
+ const dm = {
+ id: "dm-1",
+ name: "Sigma DM",
+ channelType: "dm",
+ participantPubkeys: [AGENT, AGENT_2],
+ };
+ const agents = [
+ {
+ pubkey: AGENT,
+ name: "Sigma",
+ status: "deployed",
+ agentSource: "relay",
+ canInterruptTurn: false,
+ channelIds: [],
+ channels: [],
+ },
+ ];
+
+ assert.deepEqual(
+ getChannelAgentSessionAgents({
+ activeChannel: dm,
+ activeChannelId: dm.id,
+ agents,
+ channelMembers: [],
+ }),
+ agents,
+ );
+ });
+
+ it("does not admit a known agent who is not a DM participant", () => {
+ const dm = {
+ id: "dm-1",
+ name: "Sigma DM",
+ channelType: "dm",
+ participantPubkeys: [AGENT_2],
+ };
+ const agents = [
+ {
+ pubkey: AGENT,
+ name: "Sigma",
+ status: "deployed",
+ agentSource: "relay",
+ canInterruptTurn: false,
+ channelIds: [],
+ channels: [],
+ },
+ ];
+
+ assert.deepEqual(
+ getChannelAgentSessionAgents({
+ activeChannel: dm,
+ activeChannelId: dm.id,
+ agents,
+ channelMembers: [],
+ }),
+ [],
+ );
+ });
+});
+
describe("thread-only bot typing regression", () => {
beforeEach(() => {
resetActiveAgentTurnsStore();
diff --git a/desktop/src/features/channels/ui/useChannelAgentSessions.ts b/desktop/src/features/channels/ui/useChannelAgentSessions.ts
index 8420c373279..7504b91142b 100644
--- a/desktop/src/features/channels/ui/useChannelAgentSessions.ts
+++ b/desktop/src/features/channels/ui/useChannelAgentSessions.ts
@@ -131,6 +131,14 @@ export function getChannelAgentSessionAgents({
.map((member) => normalizePubkey(member.pubkey)),
)
: null;
+ const dmParticipantPubkeys =
+ activeChannel.channelType === "dm"
+ ? new Set(
+ activeChannel.participantPubkeys.map((pubkey) =>
+ normalizePubkey(pubkey),
+ ),
+ )
+ : null;
return agents.filter((agent) => {
const normalizedPubkey = normalizePubkey(agent.pubkey);
@@ -142,6 +150,13 @@ export function getChannelAgentSessionAgents({
channelIds.includes(activeChannelId) ||
channels.includes(activeChannel.name);
+ // DMs do not reliably expose channel membership or directory channel
+ // scope. A participant that is already a known managed/relay agent must
+ // still be classified as an agent so its typing feeds the working signal.
+ if (dmParticipantPubkeys?.has(normalizedPubkey)) {
+ return true;
+ }
+
if (agent.agentSource === "member-bot") {
return botMemberPubkeys?.has(normalizedPubkey) ?? matchesDeclaredChannel;
}