diff --git a/src/message-runtime.test.ts b/src/message-runtime.test.ts index 7bc3199..cfa7378 100644 --- a/src/message-runtime.test.ts +++ b/src/message-runtime.test.ts @@ -190,127 +190,6 @@ describe('createMessageRuntime', () => { }); }); - it('ignores untriggered bot messages in paired rooms', async () => { - const chatJid = 'group@test'; - const group = makeGroup('codex'); - const channel = makeChannel(chatJid); - const saveState = vi.fn(); - const lastAgentTimestamps: Record = {}; - - vi.mocked(db.isPairedRoomJid).mockReturnValue(true); - vi.mocked(db.getMessagesSince).mockReturnValue([ - { - id: 'msg-1', - chat_jid: chatJid, - sender: 'other-bot@test', - sender_name: 'Other Bot', - content: '요청을 완료하지 못했습니다. 다시 시도해 주세요.', - timestamp: '2026-03-18T09:00:00.000Z', - is_bot_message: true, - }, - ]); - - const runtime = createMessageRuntime({ - assistantName: 'Andy', - idleTimeout: 1_000, - pollInterval: 1_000, - timezone: 'UTC', - triggerPattern: /^@Andy\b/i, - channels: [channel], - queue: { - registerProcess: vi.fn(), - closeStdin: vi.fn(), - notifyIdle: vi.fn(), - } as any, - getRegisteredGroups: () => ({ [chatJid]: group }), - getSessions: () => ({}), - getLastTimestamp: () => '', - setLastTimestamp: vi.fn(), - getLastAgentTimestamps: () => lastAgentTimestamps, - saveState, - persistSession: vi.fn(), - clearSession: vi.fn(), - }); - - const result = await runtime.processGroupMessages(chatJid, { - runId: 'run-ignore-bot-loop', - reason: 'messages', - }); - - expect(result).toBe(true); - expect(agentRunner.runAgentProcess).not.toHaveBeenCalled(); - expect(lastAgentTimestamps[chatJid]).toBe('0'); - expect(saveState).toHaveBeenCalled(); - }); - - it('processes triggered bot messages in paired rooms', async () => { - const chatJid = 'group@test'; - const group = makeGroup('codex'); - const channel = makeChannel(chatJid); - const saveState = vi.fn(); - const lastAgentTimestamps: Record = {}; - - vi.mocked(db.isPairedRoomJid).mockReturnValue(true); - vi.mocked(db.getMessagesSince).mockReturnValue([ - { - id: 'msg-1', - chat_jid: chatJid, - sender: 'other-bot@test', - sender_name: 'Other Bot', - content: '@Andy 확인 부탁', - timestamp: '2026-03-18T09:00:00.000Z', - is_bot_message: true, - }, - ]); - vi.mocked(agentRunner.runAgentProcess).mockImplementation( - async (_group, _input, _onProcess, onOutput) => { - await onOutput?.({ - status: 'success', - result: '확인했습니다.', - newSessionId: 'session-triggered-bot', - }); - return { - status: 'success', - result: '확인했습니다.', - newSessionId: 'session-triggered-bot', - }; - }, - ); - - const runtime = createMessageRuntime({ - assistantName: 'Andy', - idleTimeout: 1_000, - pollInterval: 1_000, - timezone: 'UTC', - triggerPattern: /^@Andy\b/i, - channels: [channel], - queue: { - registerProcess: vi.fn(), - closeStdin: vi.fn(), - notifyIdle: vi.fn(), - } as any, - getRegisteredGroups: () => ({ [chatJid]: group }), - getSessions: () => ({}), - getLastTimestamp: () => '', - setLastTimestamp: vi.fn(), - getLastAgentTimestamps: () => lastAgentTimestamps, - saveState, - persistSession: vi.fn(), - clearSession: vi.fn(), - }); - - const result = await runtime.processGroupMessages(chatJid, { - runId: 'run-triggered-bot-message', - reason: 'messages', - }); - - expect(result).toBe(true); - expect(agentRunner.runAgentProcess).toHaveBeenCalledTimes(1); - expect(channel.sendMessage).toHaveBeenCalledWith(chatJid, '확인했습니다.'); - expect(lastAgentTimestamps[chatJid]).toBe('1'); - expect(saveState).toHaveBeenCalled(); - }); - it('clears Claude sessions and closes stdin immediately on poisoned output', async () => { const chatJid = 'group@test'; const group = makeGroup('claude-code'); diff --git a/src/message-runtime.ts b/src/message-runtime.ts index f0c64a7..54d7f72 100644 --- a/src/message-runtime.ts +++ b/src/message-runtime.ts @@ -126,21 +126,6 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): { channel?.isOwnMessage?.bind(channel), ); - const filterTriggeredPairedBotMessages = ( - chatJid: string, - messages: Parameters[0], - ) => { - if (!isPairedRoomJid(chatJid)) return messages; - - // In paired rooms, require an explicit trigger before reacting to another - // bot's message. This keeps the room usable without reintroducing bot loops. - return messages.filter( - (message) => - !message.is_bot_message || - deps.triggerPattern.test(message.content.trim()), - ); - }; - const deliverOpenWorkItem = async ( channel: Channel, item: WorkItem, @@ -590,9 +575,10 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): { sinceSeqCursor, deps.assistantName, ); - const missedMessages = filterTriggeredPairedBotMessages( + const missedMessages = getProcessableMessages( chatJid, - getProcessableMessages(chatJid, rawMissedMessages, channel), + rawMissedMessages, + channel, ); if (missedMessages.length === 0) {