initialize Electron desktop app and remove unused files
This commit is contained in:
@@ -0,0 +1,215 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const { mockIo, mockSocket } = vi.hoisted(() => {
|
||||
const mockSocket: any = {
|
||||
id: 'agent-socket-1',
|
||||
connected: true,
|
||||
io: { on: vi.fn() },
|
||||
on: vi.fn((event: string, handler: (...args: any[]) => void) => {
|
||||
if (event === 'connect') queueMicrotask(() => handler())
|
||||
return mockSocket
|
||||
}),
|
||||
emit: vi.fn(),
|
||||
disconnect: vi.fn(),
|
||||
}
|
||||
return {
|
||||
mockSocket,
|
||||
mockIo: vi.fn(() => mockSocket),
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('socket.io-client', () => ({
|
||||
io: mockIo,
|
||||
}))
|
||||
|
||||
vi.mock('../../packages/server/src/services/auth', () => ({
|
||||
getToken: vi.fn(async () => 'test-token'),
|
||||
}))
|
||||
|
||||
import { ContextEngine } from '../../packages/server/src/services/hermes/context-engine/compressor'
|
||||
import type {
|
||||
GatewayCaller,
|
||||
MessageFetcher,
|
||||
StoredMessage,
|
||||
} from '../../packages/server/src/services/hermes/context-engine/types'
|
||||
import { AgentClients } from '../../packages/server/src/services/hermes/group-chat/agent-clients'
|
||||
import {
|
||||
buildProjectedGroupChatHistory,
|
||||
projectGroupChatMessage,
|
||||
} from '../../packages/server/src/services/hermes/group-chat/context-projection'
|
||||
|
||||
function makeMessage(overrides: Partial<StoredMessage>): StoredMessage {
|
||||
return {
|
||||
id: 'm1',
|
||||
roomId: 'room-1',
|
||||
senderId: 'user-1',
|
||||
senderName: 'Alice',
|
||||
content: 'hello',
|
||||
timestamp: 1,
|
||||
role: 'user',
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
describe('group chat context projection', () => {
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('projects own agent messages as assistant and other participants with attribution as user', () => {
|
||||
expect(projectGroupChatMessage(makeMessage({ senderId: 'agent-1', senderName: 'Worker', role: 'assistant', content: '@Bob I handled it' }), {
|
||||
agentId: 'agent-1',
|
||||
socketId: 'agent-socket-1',
|
||||
name: 'Worker',
|
||||
})).toEqual({ role: 'assistant', content: '[Worker]: I handled it' })
|
||||
|
||||
expect(projectGroupChatMessage(makeMessage({ senderId: 'user-2', senderName: 'Alice', role: 'user', content: '@Worker please help' }), {
|
||||
socketId: 'agent-socket-1',
|
||||
name: 'Worker',
|
||||
})).toEqual({ role: 'user', content: '[Alice]: please help' })
|
||||
})
|
||||
|
||||
it('does not project same-name humans as the own agent when sender ids differ', () => {
|
||||
expect(projectGroupChatMessage(makeMessage({
|
||||
senderId: 'user-2',
|
||||
senderName: 'Worker',
|
||||
role: 'user',
|
||||
content: '@Worker I am a different participant',
|
||||
}), {
|
||||
socketId: 'agent-socket-1',
|
||||
name: 'Worker',
|
||||
})).toEqual({ role: 'user', content: '[Worker]: I am a different participant' })
|
||||
})
|
||||
|
||||
it('formats tool results and assistant tool calls consistently', () => {
|
||||
expect(projectGroupChatMessage(makeMessage({
|
||||
senderName: 'Worker',
|
||||
senderId: 'agent-socket-1',
|
||||
role: 'tool',
|
||||
tool_name: 'search',
|
||||
content: 'found docs',
|
||||
}), {
|
||||
socketId: 'agent-socket-1',
|
||||
name: 'Worker',
|
||||
})).toEqual({ role: 'user', content: '[Worker] [Tool result: search]\nfound docs' })
|
||||
|
||||
expect(projectGroupChatMessage(makeMessage({
|
||||
senderName: 'Reviewer',
|
||||
senderId: 'agent-reviewer',
|
||||
role: 'assistant',
|
||||
content: '@Worker let me check',
|
||||
tool_calls: [{ function: { name: 'search', arguments: '{"q":"docs"}' } }],
|
||||
}), {
|
||||
socketId: 'agent-socket-1',
|
||||
name: 'Worker',
|
||||
})).toEqual({
|
||||
role: 'user',
|
||||
content: '[Reviewer]: let me check\n[Reviewer]: [Calling tool: search with arguments: {"q":"docs"}]',
|
||||
})
|
||||
})
|
||||
|
||||
it('preserves summary prefix while stripping mentions from projected content', () => {
|
||||
const history = buildProjectedGroupChatHistory('Earlier summary', [
|
||||
makeMessage({ senderName: 'Alice', content: '@Worker compare this with @Bob' }),
|
||||
], {
|
||||
socketId: 'agent-socket-1',
|
||||
name: 'Worker',
|
||||
})
|
||||
|
||||
expect(history).toEqual([
|
||||
{ role: 'user', content: '[Previous conversation summary]\nEarlier summary' },
|
||||
{ role: 'assistant', content: 'I have reviewed the conversation history and understand the context.' },
|
||||
{ role: 'user', content: '[Alice]: compare this with ' },
|
||||
])
|
||||
})
|
||||
|
||||
it('uses the same projection semantics for actual model context and final room token estimates', async () => {
|
||||
const messages = [
|
||||
makeMessage({ id: 'm1', senderName: 'Alice', senderId: 'user-1', role: 'user', content: '@Worker please compare options' }),
|
||||
makeMessage({ id: 'm2', senderName: 'Worker', senderId: 'agent-1', role: 'assistant', content: '@Bob I will take this' }),
|
||||
makeMessage({ id: 'm3', senderName: 'Reviewer', senderId: 'agent-reviewer', role: 'assistant', content: '@Worker checking', tool_calls: [{ function: { name: 'search', arguments: '{"q":"options"}' } }], timestamp: 3 }),
|
||||
makeMessage({ id: 'm4', senderName: 'Worker', senderId: 'agent-1', role: 'tool', tool_name: 'search', content: 'docs found', timestamp: 4 }),
|
||||
]
|
||||
|
||||
const fetcher: MessageFetcher = {
|
||||
getMessagesForContext: vi.fn(() => messages),
|
||||
getContextSnapshot: vi.fn(() => null),
|
||||
saveContextSnapshot: vi.fn(),
|
||||
deleteContextSnapshot: vi.fn(),
|
||||
}
|
||||
const gatewayCaller: GatewayCaller = {
|
||||
summarize: vi.fn(),
|
||||
}
|
||||
const engine = new ContextEngine({
|
||||
config: { triggerTokens: 100_000, maxHistoryTokens: 32_000, tailMessageCount: 10, charsPerToken: 4, summarizationTimeoutMs: 30_000 },
|
||||
messageFetcher: fetcher,
|
||||
gatewayCaller,
|
||||
})
|
||||
|
||||
const result = await engine.buildContext({
|
||||
roomId: 'room-1',
|
||||
agentId: 'agent-1',
|
||||
agentName: 'Worker',
|
||||
agentDescription: '',
|
||||
agentSocketId: 'agent-socket-1',
|
||||
roomName: 'general',
|
||||
memberNames: ['Alice', 'Worker', 'Reviewer'],
|
||||
members: [],
|
||||
upstream: '',
|
||||
apiKey: null,
|
||||
currentMessage: messages[messages.length - 1],
|
||||
})
|
||||
|
||||
const clients = new AgentClients()
|
||||
const client = await clients.createAgent({
|
||||
agentId: 'agent-1',
|
||||
profile: 'default',
|
||||
name: 'Worker',
|
||||
description: '',
|
||||
invited: 0,
|
||||
} as any)
|
||||
client.setStorage({
|
||||
getMessagesForContext: vi.fn(() => messages),
|
||||
} as any)
|
||||
|
||||
expect((client as any).buildRoomEstimateHistory('room-1')).toEqual(result.conversationHistory)
|
||||
|
||||
client.disconnect()
|
||||
})
|
||||
|
||||
it('includes persisted summary snapshots in final room token estimate history', async () => {
|
||||
const messages = [
|
||||
makeMessage({ id: 'm1', senderName: 'Alice', senderId: 'user-1', role: 'user', content: 'older request', timestamp: 1 }),
|
||||
makeMessage({ id: 'm2', senderName: 'Worker', senderId: 'agent-1', role: 'assistant', content: 'old answer', timestamp: 2 }),
|
||||
makeMessage({ id: 'm3', senderName: 'Alice', senderId: 'user-1', role: 'user', content: '@Worker latest request', timestamp: 3 }),
|
||||
]
|
||||
|
||||
const clients = new AgentClients()
|
||||
const client = await clients.createAgent({
|
||||
agentId: 'agent-1',
|
||||
profile: 'default',
|
||||
name: 'Worker',
|
||||
description: '',
|
||||
invited: 0,
|
||||
} as any)
|
||||
client.setStorage({
|
||||
getMessagesForContext: vi.fn(() => messages),
|
||||
getContextSnapshot: vi.fn(() => ({
|
||||
roomId: 'room-1',
|
||||
summary: 'Earlier room summary',
|
||||
lastMessageId: 'm1',
|
||||
lastMessageTimestamp: 1,
|
||||
updatedAt: Date.now(),
|
||||
})),
|
||||
} as any)
|
||||
|
||||
expect((client as any).buildRoomEstimateHistory('room-1')).toEqual([
|
||||
{ role: 'user', content: '[Previous conversation summary]\nEarlier room summary' },
|
||||
{ role: 'assistant', content: 'I have reviewed the conversation history and understand the context.' },
|
||||
{ role: 'assistant', content: '[Worker]: old answer' },
|
||||
{ role: 'user', content: '[Alice]: latest request' },
|
||||
])
|
||||
|
||||
client.disconnect()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user