81 lines
4.1 KiB
TypeScript
81 lines
4.1 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import {
|
|
isAllAgentsMentioned,
|
|
isAgentMentioned,
|
|
isReservedMentionName,
|
|
resolveMentionTargets,
|
|
stripMentionRoutingTokens,
|
|
} from '../../packages/server/src/services/hermes/group-chat/mention-routing'
|
|
|
|
type TestAgent = { name: string; id?: string; agentId?: string; profile?: string }
|
|
|
|
const agents: TestAgent[] = [
|
|
{ name: 'Alice', id: 'socket-alice', agentId: 'agent-alice' },
|
|
{ name: 'Bob', id: 'socket-bob', agentId: 'agent-bob' },
|
|
{ name: 'Regex.Bot', id: 'socket-regex', agentId: 'agent-regex' },
|
|
]
|
|
|
|
describe('group chat mention routing', () => {
|
|
it('reserves @all so it cannot be confused with a literal agent name', () => {
|
|
expect(isReservedMentionName('all')).toBe(true)
|
|
expect(isReservedMentionName(' ALL ')).toBe(true)
|
|
expect(isReservedMentionName('Alice')).toBe(false)
|
|
})
|
|
|
|
it('recognizes @all as a standalone mention with safe boundaries', () => {
|
|
expect(isAllAgentsMentioned('@all please compare notes')).toBe(true)
|
|
expect(isAllAgentsMentioned('please compare notes @ALL')).toBe(true)
|
|
expect(isAllAgentsMentioned('@all, compare notes')).toBe(true)
|
|
expect(isAllAgentsMentioned('email user@all.example')).toBe(false)
|
|
expect(isAllAgentsMentioned('@alligator should not notify everyone')).toBe(false)
|
|
expect(isAllAgentsMentioned('prefix@all should not notify everyone')).toBe(false)
|
|
})
|
|
|
|
it('keeps exact agent mentions boundary-aware and regex-safe', () => {
|
|
expect(isAgentMentioned('@Regex.Bot please review', 'Regex.Bot')).toBe(true)
|
|
expect(isAgentMentioned('@RegexxBot should not match', 'Regex.Bot')).toBe(false)
|
|
expect(isAgentMentioned('@Alice, please review', 'Alice')).toBe(true)
|
|
expect(isAgentMentioned('mailto@Alice.example', 'Alice')).toBe(false)
|
|
})
|
|
|
|
it('routes @all to every room agent except the sender identity', () => {
|
|
expect(resolveMentionTargets(agents, '@all summarize the options', 'socket-alice').map(a => a.name)).toEqual(['Bob', 'Regex.Bot'])
|
|
})
|
|
|
|
it('keeps same-name human senders routable because sender exclusion uses identity, not display name', () => {
|
|
const sameNameAgents: TestAgent[] = [
|
|
{ name: 'test', id: 'socket-agent-test', agentId: 'agent-test' },
|
|
{ name: 'tt', id: 'socket-agent-tt', agentId: 'agent-tt' },
|
|
]
|
|
|
|
expect(resolveMentionTargets(sameNameAgents, '@all can you talk to me?', 'human-test-user').map(a => a.name)).toEqual(['test', 'tt'])
|
|
expect(resolveMentionTargets(sameNameAgents, '@test why no response?', 'human-test-user').map(a => a.name)).toEqual(['test'])
|
|
})
|
|
|
|
it('still excludes an agent from routing to itself when the sender identity matches that agent', () => {
|
|
const sameNameAgents: TestAgent[] = [
|
|
{ name: 'test', id: 'socket-agent-test', agentId: 'agent-test' },
|
|
{ name: 'tt', id: 'socket-agent-tt', agentId: 'agent-tt' },
|
|
]
|
|
|
|
expect(resolveMentionTargets(sameNameAgents, '@all compare plans', 'socket-agent-test').map(a => a.name)).toEqual(['tt'])
|
|
expect(resolveMentionTargets(sameNameAgents, '@all compare plans', 'agent-test').map(a => a.name)).toEqual(['tt'])
|
|
expect(resolveMentionTargets(sameNameAgents, '@test check yourself', 'socket-agent-test').map(a => a.name)).toEqual([])
|
|
})
|
|
|
|
it('routes explicit mentions without treating partial @all text as broadcast', () => {
|
|
expect(resolveMentionTargets(agents, '@Bob and @Regex.Bot compare plans', 'socket-alice').map(a => a.name)).toEqual(['Bob', 'Regex.Bot'])
|
|
expect(resolveMentionTargets(agents, '@alligator and @Bob compare plans', 'socket-alice').map(a => a.name)).toEqual(['Bob'])
|
|
})
|
|
|
|
it('dedupes mixed @all and explicit mentions', () => {
|
|
expect(resolveMentionTargets(agents, '@all @Bob compare plans', 'socket-alice').map(a => a.name)).toEqual(['Bob', 'Regex.Bot'])
|
|
})
|
|
|
|
it('strips the broadcast token and this agent mention before routing to the model', () => {
|
|
expect(stripMentionRoutingTokens('@all @Bob please review', 'Bob')).toBe('please review')
|
|
expect(stripMentionRoutingTokens('@ALL, @Regex.Bot: please review', 'Regex.Bot')).toBe('please review')
|
|
expect(stripMentionRoutingTokens('@all please review', 'all')).toBe('please review')
|
|
})
|
|
})
|