2026-07-05 21:10:14 +02:00
import test from 'node:test' ;
import assert from 'node:assert/strict' ;
import { TerminalAgent , TerminalToolRegistry } from '../lib/agent/terminal-agent.mjs' ;
2026-07-07 18:46:44 +02:00
import { createTerminalToolRegistry } from '../lib/agent/terminal-tools.mjs' ;
2026-07-05 21:10:14 +02:00
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' ) ;
} ) ;
2026-07-05 22:11:15 +02:00
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/ ) ;
2026-07-05 22:28:40 +02:00
assert . match ( prompts [ 0 ] , /Your name is DAVE/ ) ;
assert . match ( prompts [ 0 ] , /ADAPTIVE WRITING STYLE/ ) ;
assert . match ( prompts [ 1 ] , /Your name is DAVE/ ) ;
2026-07-05 22:11:15 +02:00
} ) ;
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 ) ;
} ) ;
2026-07-05 22:47:08 +02:00
2026-07-11 18:27:12 +02:00
test ( 'empty model responses are repaired instead of shown to the operator' , async ( ) => {
let call = 0 ;
const agent = new TerminalAgent ( {
provider : {
isConfigured : true ,
async complete ( ) {
call ++ ;
return call === 1
? { text : '' }
: { text : JSON . stringify ( { type : 'final' , answer : 'Ich pruefe den letzten Sweep weiter.' , confidence : 'low' , evidence : [ ] , notify : false , priority : 'routine' } ) } ;
} ,
} ,
registry : new TerminalToolRegistry ( [ ] ) ,
} ) ;
const result = await agent . run ( 'Ist noch woanders was passiert?' , { preferredLanguage : 'de' } ) ;
assert . equal ( result . answer , 'Ich pruefe den letzten Sweep weiter.' ) ;
assert . doesNotMatch ( result . answer , /no usable response/i ) ;
} ) ;
test ( 'provider timeout falls back to the local sweep snapshot' , async ( ) => {
const agent = new TerminalAgent ( {
provider : {
isConfigured : true ,
async complete ( ) {
throw new Error ( 'The operation was aborted due to timeout' ) ;
} ,
} ,
registry : new TerminalToolRegistry ( [ ] ) ,
} ) ;
const context = JSON . stringify ( {
news : [ { title : 'Regional escalation report' } ] ,
urgentOsint : [ 'OSINT: power outage near border' ] ,
degradedSources : [ { name : 'GDELT' } , { name : 'ACLED' } ] ,
ideas : [ { title : 'Gold hedge' } ] ,
} ) ;
const result = await agent . run ( 'Auch auf andere Teile der Welt' , { preferredLanguage : 'de' , context } ) ;
assert . match ( result . answer , /keine stabile Modellantwort/ ) ;
assert . match ( result . answer , /letzten lokalen Sweep/ ) ;
assert . match ( result . answer , /OSINT: power outage near border/ ) ;
assert . match ( result . answer , /Steuern kann ich per Chat/ ) ;
assert . equal ( result . trace [ 0 ] . tool , 'llm' ) ;
assert . equal ( result . trace [ 0 ] . status , 'failed' ) ;
} ) ;
2026-07-06 09:14:55 +02:00
test ( 'agent propagates preferred German language through tool and finalization prompts' , async ( ) => {
const prompts = [ ] ;
let call = 0 ;
const agent = new TerminalAgent ( {
provider : {
isConfigured : true ,
async complete ( systemPrompt ) {
prompts . push ( systemPrompt ) ;
call ++ ;
return call === 1
? { text : JSON . stringify ( { type : 'tool_call' , tool : 'get_status' , arguments : { } , rationale : 'Status prüfen' } ) }
: { text : JSON . stringify ( { type : 'final' , answer : 'Die Systemlage ist stabil.' , confidence : 'high' , evidence : [ ] , notify : false , priority : 'routine' } ) } ;
} ,
} ,
registry : new TerminalToolRegistry ( [ { name : 'get_status' , handler : async ( ) => ( { status : 'healthy' } ) } ] ) ,
maxSteps : 1 ,
} ) ;
const result = await agent . run ( 'Lage?' , { preferredLanguage : 'de' } ) ;
assert . equal ( result . answer , 'Die Systemlage ist stabil.' ) ;
assert . match ( prompts [ 0 ] , /REQUIRED RESPONSE LANGUAGE: German \(de\)/ ) ;
assert . match ( prompts [ 1 ] , /REQUIRED RESPONSE LANGUAGE: German \(de\)/ ) ;
} ) ;
2026-07-05 22:47:08 +02:00
test ( 'scheduled presence cannot create pending mutating actions' , async ( ) => {
let executions = 0 ;
const agent = new TerminalAgent ( {
provider : providerWith ( [
{ type : 'tool_call' , tool : 'trigger_sweep' , arguments : { } , rationale : 'Refresh data' } ,
{ type : 'final' , answer : 'No autonomous action was taken.' , confidence : 'high' , evidence : [ ] , notify : false , priority : 'routine' } ,
] ) ,
registry : new TerminalToolRegistry ( [ {
name : 'trigger_sweep' ,
mutating : true ,
handler : async ( ) => { executions ++ ; } ,
} ] ) ,
} ) ;
const result = await agent . run ( 'Evaluate presence' , { mode : 'presence' } ) ;
assert . equal ( result . pendingAction , undefined ) ;
assert . equal ( result . trace [ 0 ] . status , 'rejected' ) ;
assert . equal ( executions , 0 ) ;
} ) ;
2026-07-05 23:14:29 +02:00
test ( 'ChatML-style tagged tool calls are executed without leaking protocol text' , async ( ) => {
const calls = [ ] ;
let responseIndex = 0 ;
const responses = [
'<|tool_call>call:get_evidence{query:"Russia planned attack military escalation",limit:5}<tool_call|>' ,
JSON . stringify ( { type : 'final' , answer : 'The retrieved evidence does not confirm a specific planned attack.' , confidence : 'medium' , evidence : [ 'evt-russia' ] , notify : false , priority : 'routine' } ) ,
] ;
const agent = new TerminalAgent ( {
provider : {
isConfigured : true ,
async complete ( ) { return { text : responses [ responseIndex ++ ] } ; } ,
} ,
registry : new TerminalToolRegistry ( [ {
name : 'get_evidence' ,
handler : async args => { calls . push ( args ) ; return [ { id : 'evt-russia' } ] ; } ,
} ] ) ,
} ) ;
const result = await agent . run ( 'Wie sieht es mit dem angeblich geplanten Angriff von Russland aus?' ) ;
assert . deepEqual ( calls , [ { query : 'Russia planned attack military escalation' , limit : 5 } ] ) ;
assert . equal ( result . trace [ 0 ] . tool , 'get_evidence' ) ;
assert . doesNotMatch ( result . answer , /tool_call|call:get_evidence/i ) ;
} ) ;
test ( 'malformed tagged protocol is repaired instead of returned to the user' , async ( ) => {
let responseIndex = 0 ;
const responses = [
'<|tool_call>call:get_evidence{broken arguments}<tool_call|>' ,
JSON . stringify ( { type : 'final' , answer : 'I could not run the malformed tool request.' , confidence : 'low' , evidence : [ ] , notify : false , priority : 'routine' } ) ,
] ;
const agent = new TerminalAgent ( {
provider : { isConfigured : true , async complete ( ) { return { text : responses [ responseIndex ++ ] } ; } } ,
registry : new TerminalToolRegistry ( [ ] ) ,
} ) ;
const result = await agent . run ( 'Check the claim' ) ;
assert . equal ( result . answer , 'I could not run the malformed tool request.' ) ;
assert . doesNotMatch ( result . answer , /tool_call|call:get_evidence/i ) ;
} ) ;
2026-07-07 18:46:44 +02:00
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 ;
}
} ) ;