feat: add reply/quoted message context support

Add generic reply context fields to NewMessage (reply_to_message_id,
reply_to_message_content, reply_to_sender_name) so any channel can
pass quoted message context to the agent.

- Add thread_id and reply_to_* fields to NewMessage interface
- Add DB migration for reply context columns on messages table
- Update storeMessage/getMessagesSince/getNewMessages to persist and
  retrieve reply fields
- Render reply context as <quoted_message> XML in formatMessages
- Add DB and formatting tests

Co-Authored-By: Alfred-the-buttler <leon.alfred.bot@gmail.com>
Co-Authored-By: moktamd <moktamd@users.noreply.github.com>
Co-Authored-By: gurixs-carson <gurixs-carson@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
exe.dev user
2026-04-02 17:05:24 +00:00
parent 7b337a7a07
commit ee599b9f0c
5 changed files with 171 additions and 4 deletions

View File

@@ -142,6 +142,86 @@ describe('storeMessage', () => {
});
});
// --- reply context persistence ---
describe('reply context', () => {
it('stores and retrieves reply_to fields', () => {
storeChatMetadata('group@g.us', '2024-01-01T00:00:00.000Z');
storeMessage({
id: 'reply-1',
chat_jid: 'group@g.us',
sender: '123',
sender_name: 'Alice',
content: 'Yes, on my way!',
timestamp: '2024-01-01T00:00:01.000Z',
reply_to_message_id: '42',
reply_to_message_content: 'Are you coming tonight?',
reply_to_sender_name: 'Bob',
});
const messages = getMessagesSince(
'group@g.us',
'2024-01-01T00:00:00.000Z',
'Andy',
);
expect(messages).toHaveLength(1);
expect(messages[0].reply_to_message_id).toBe('42');
expect(messages[0].reply_to_message_content).toBe(
'Are you coming tonight?',
);
expect(messages[0].reply_to_sender_name).toBe('Bob');
});
it('returns null for messages without reply context', () => {
storeChatMetadata('group@g.us', '2024-01-01T00:00:00.000Z');
store({
id: 'no-reply',
chat_jid: 'group@g.us',
sender: '123',
sender_name: 'Alice',
content: 'Just a normal message',
timestamp: '2024-01-01T00:00:01.000Z',
});
const messages = getMessagesSince(
'group@g.us',
'2024-01-01T00:00:00.000Z',
'Andy',
);
expect(messages).toHaveLength(1);
expect(messages[0].reply_to_message_id).toBeNull();
expect(messages[0].reply_to_message_content).toBeNull();
expect(messages[0].reply_to_sender_name).toBeNull();
});
it('retrieves reply context via getNewMessages', () => {
storeChatMetadata('group@g.us', '2024-01-01T00:00:00.000Z');
storeMessage({
id: 'reply-2',
chat_jid: 'group@g.us',
sender: '456',
sender_name: 'Carol',
content: 'Agreed',
timestamp: '2024-01-01T00:00:01.000Z',
reply_to_message_id: '99',
reply_to_message_content: 'We should meet',
reply_to_sender_name: 'Dave',
});
const { messages } = getNewMessages(
['group@g.us'],
'2024-01-01T00:00:00.000Z',
'Andy',
);
expect(messages).toHaveLength(1);
expect(messages[0].reply_to_message_id).toBe('99');
expect(messages[0].reply_to_sender_name).toBe('Dave');
});
});
// --- getMessagesSince ---
describe('getMessagesSince', () => {