feat: make DAVE Telegram chat conversational
This commit is contained in:
@@ -117,6 +117,35 @@ test('Telegram transport routes authorized free text as plain-text AI reply', as
|
||||
assert.equal(sent.body.reply_to_message_id, 11);
|
||||
});
|
||||
|
||||
test('Telegram conversation delivery strips visible markdown and splits large AI replies', async () => {
|
||||
const alerter = new TelegramAlerter({ botToken: 'test-token', chatId: '42' });
|
||||
const requests = [];
|
||||
const originalFetch = globalThis.fetch;
|
||||
globalThis.fetch = async (url, options = {}) => {
|
||||
requests.push({ url, body: options.body ? JSON.parse(options.body) : null });
|
||||
return { ok: true, json: async () => ({ ok: true, result: { message_id: requests.length } }) };
|
||||
};
|
||||
|
||||
try {
|
||||
const text = '**Aktuelle Lage:**\n\n' + [
|
||||
'Erster kurzer Absatz mit einer direkten Einschaetzung.',
|
||||
'Zweiter Absatz mit mehr Kontext und genug Laenge, damit er in einer eigenen Nachricht landet. '.repeat(6),
|
||||
'Dritter Absatz mit einer Rueckfrage, auf die der Nutzer direkt antworten kann. '.repeat(5),
|
||||
].join('\n\n');
|
||||
const result = await alerter.sendConversation(text, { maxChunkChars: 300, delayMs: 0, replyToMessageId: 7 });
|
||||
assert.equal(result.ok, true);
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
|
||||
const sent = requests.filter(request => request.url.includes('/sendMessage'));
|
||||
assert.ok(sent.length >= 2);
|
||||
assert.doesNotMatch(sent[0].body.text, /\*\*/);
|
||||
assert.equal('parse_mode' in sent[0].body, false);
|
||||
assert.equal(sent[0].body.reply_to_message_id, 7);
|
||||
assert.equal('reply_to_message_id' in sent[1].body, false);
|
||||
});
|
||||
|
||||
test('Telegram OSINT extraction excludes private AI chat messages', () => {
|
||||
const messages = extractBotChannelMessages([
|
||||
{ update_id: 1, message: { text: 'private question', chat: { id: 42, type: 'private' } } },
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { TerminalAgent, TerminalToolRegistry } from '../lib/agent/terminal-agent.mjs';
|
||||
import { createTerminalToolRegistry } from '../lib/agent/terminal-tools.mjs';
|
||||
|
||||
function providerWith(decisions) {
|
||||
let index = 0;
|
||||
@@ -214,3 +215,38 @@ test('malformed tagged protocol is repaired instead of returned to the user', as
|
||||
assert.equal(result.answer, 'I could not run the malformed tool request.');
|
||||
assert.doesNotMatch(result.answer, /tool_call|call:get_evidence/i);
|
||||
});
|
||||
|
||||
test('terminal registry exposes bounded public web search as a read-only tool', async () => {
|
||||
const originalFetch = globalThis.fetch;
|
||||
let requestedUrl = '';
|
||||
globalThis.fetch = async (url) => {
|
||||
requestedUrl = String(url);
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
headers: { get: () => 'application/json' },
|
||||
text: async () => JSON.stringify({
|
||||
articles: [{
|
||||
title: 'Verified report',
|
||||
sourceCommonName: 'Example News',
|
||||
url: 'https://example.test/report',
|
||||
seendate: '20260707100000',
|
||||
language: 'English',
|
||||
}],
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
try {
|
||||
const registry = createTerminalToolRegistry();
|
||||
const tool = registry.get('web_search');
|
||||
assert.equal(tool.mutating, false);
|
||||
const result = await registry.execute('web_search', { query: 'Russia attack claim', limit: 3, hours: 24 });
|
||||
assert.match(requestedUrl, /api\.gdeltproject\.org\/api\/v2\/doc\/doc/);
|
||||
assert.equal(result.query, 'Russia attack claim');
|
||||
assert.equal(result.results.length, 1);
|
||||
assert.equal(result.results[0].url, 'https://example.test/report');
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user