Files
intelligence-terminal/test/terminal-agent.test.mjs
MrSphay 994c806ea3
All checks were successful
Codex Template Compliance / template-compliance (pull_request) Successful in 7s
Build / test-and-image (pull_request) Successful in 46s
feat: add DAVE adaptive synthetic persona
2026-07-05 22:28:40 +02:00

133 lines
5.7 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { TerminalAgent, TerminalToolRegistry } from '../lib/agent/terminal-agent.mjs';
function providerWith(decisions) {
let index = 0;
return {
isConfigured: true,
async complete() {
const decision = decisions[Math.min(index++, decisions.length - 1)];
return { text: JSON.stringify(decision) };
},
};
}
test('terminal agent performs bounded multi-step tool reasoning', async () => {
const registry = new TerminalToolRegistry([
{ name: 'get_status', description: 'status', handler: async () => ({ status: 'degraded' }) },
{ name: 'search_memory', description: 'memory', handler: async args => ({ query: args.query, hits: 2 }) },
]);
const agent = new TerminalAgent({
registry,
provider: providerWith([
{ type: 'tool_call', tool: 'get_status', arguments: {}, rationale: 'Check freshness' },
{ type: 'tool_call', tool: 'search_memory', arguments: { query: 'Iran' }, rationale: 'Compare history' },
{ type: 'final', answer: 'Two historical events support the current signal.', confidence: 'medium', evidence: ['evt-1'], notify: false, priority: 'routine' },
]),
maxSteps: 4,
});
const result = await agent.run('What changed?', { chatId: 42 });
assert.equal(result.answer, 'Two historical events support the current signal.');
assert.equal(result.confidence, 'medium');
assert.deepEqual(result.trace.map(item => item.tool), ['get_status', 'search_memory']);
assert.ok(result.trace.every(item => item.status === 'ok'));
});
test('mutating tools require chat-bound confirmation', async () => {
let executions = 0;
const registry = new TerminalToolRegistry([{
name: 'trigger_sweep',
description: 'sweep',
mutating: true,
handler: async (_args, runtime) => {
assert.equal(runtime.confirmed, true);
executions++;
return { accepted: true };
},
}]);
const agent = new TerminalAgent({
registry,
provider: providerWith([{ type: 'tool_call', tool: 'trigger_sweep', arguments: {}, rationale: 'Fresh data needed' }]),
});
const proposal = await agent.run('Run a sweep', { chatId: 42 });
assert.equal(executions, 0);
assert.equal(proposal.pendingAction.tool, 'trigger_sweep');
assert.equal((await agent.confirm(proposal.pendingAction.id, 99)).ok, false);
assert.equal(executions, 0);
assert.equal((await agent.confirm(proposal.pendingAction.id, 42)).ok, true);
assert.equal(executions, 1);
assert.equal((await agent.confirm(proposal.pendingAction.id, 42)).ok, false);
});
test('unknown tools fail closed and remain in audit trace', async () => {
const agent = new TerminalAgent({
registry: new TerminalToolRegistry([]),
provider: providerWith([
{ type: 'tool_call', tool: 'run_shell', arguments: { command: 'whoami' }, rationale: 'Not allowed' },
{ type: 'final', answer: 'That operation is not available.', confidence: 'high', evidence: [], notify: false, priority: 'routine' },
]),
});
const result = await agent.run('Run shell', { chatId: 42 });
assert.equal(result.answer, 'That operation is not available.');
assert.deepEqual(result.trace[0], { tool: 'run_shell', status: 'rejected', durationMs: 0, rationale: 'Not allowed' });
});
test('proactive notifications observe cooldown', async () => {
const agent = new TerminalAgent({
registry: new TerminalToolRegistry([]),
provider: providerWith([{ type: 'final', answer: 'Material escalation detected.', confidence: 'high', evidence: ['https://example.test'], notify: true, priority: 'flash' }]),
proactiveCooldownMs: 60000,
});
const first = await agent.analyzeProactively('Evaluate');
const second = await agent.analyzeProactively('Evaluate again');
assert.equal(first.notify, true);
assert.equal(first.priority, 'flash');
assert.equal(second.notify, false);
assert.equal(second.suppressed, 'cooldown');
});
test('step exhaustion uses a final-only prompt and returns synthesized evidence', async () => {
const prompts = [];
let call = 0;
const provider = {
isConfigured: true,
async complete(system) {
prompts.push(system);
call++;
return call === 1
? { text: JSON.stringify({ type: 'tool_call', tool: 'get_evidence', arguments: {}, rationale: 'Verify claim' }) }
: { text: JSON.stringify({ type: 'final', answer: 'The available evidence does not confirm the claim.', confidence: 'medium', evidence: ['evt-1'], notify: false, priority: 'routine' }) };
},
};
const agent = new TerminalAgent({
provider,
registry: new TerminalToolRegistry([{ name: 'get_evidence', handler: async () => [{ id: 'evt-1' }] }]),
maxSteps: 1,
});
const result = await agent.run('Is the claim confirmed?');
assert.equal(result.answer, 'The available evidence does not confirm the claim.');
assert.match(prompts[1], /Tool use is finished and unavailable/);
assert.doesNotMatch(prompts[1], /ALLOWLISTED TOOLS/);
assert.match(prompts[0], /Your name is DAVE/);
assert.match(prompts[0], /ADAPTIVE WRITING STYLE/);
assert.match(prompts[1], /Your name is DAVE/);
});
test('repeated tool calls during finalization fail closed without leaking protocol JSON', async () => {
const provider = providerWith([{ type: 'tool_call', tool: 'get_evidence', arguments: {}, rationale: 'Keep searching' }]);
const agent = new TerminalAgent({
provider,
registry: new TerminalToolRegistry([{ name: 'get_evidence', handler: async () => [] }]),
maxSteps: 1,
});
const result = await agent.run('Wie sieht es mit dem Angriff aus?');
assert.match(result.answer, /nicht zuverlässig/);
assert.doesNotMatch(result.answer, /tool_call|get_evidence|rationale/i);
assert.equal(result.notify, false);
});