64 lines
4.6 KiB
JavaScript
64 lines
4.6 KiB
JavaScript
const {test}=require('node:test');
|
|
const assert=require('node:assert/strict');
|
|
const ale=require('../.test-build/ale-store.js');
|
|
const llm=require('../.test-build/llm/index.js');
|
|
const {runLiveAutonomyTick}=require('../.test-build/autonomy.js');
|
|
|
|
test('inference preserves intervening observations and cancels after retirement',async()=>{
|
|
const now=Date.now();
|
|
const bot={id:'hero',name:'Zaku',status:'online',tier:'hero',decisions:0,
|
|
personality:{activities:['explore']},autonomy:{managed:true},level:5,zone:'Old zone'};
|
|
let state={revision:1,bots:[bot],events:[],sequence:0,settings:{hardcore:{enabled:false}}};
|
|
const store={load:async()=>structuredClone(state),save:async(s,revision)=>{
|
|
assert.equal(revision,state.revision);state=s;
|
|
}};
|
|
ale.recentAleEvents=async()=>[{at:new Date(now).toISOString(),kind:'player.snapshot',payload:{name:'Zaku',isBot:true,level:5,zoneName:'Old zone'}}];
|
|
let queued=0,retire=false;
|
|
ale.queueAleCommand=async()=>{queued++;return {id:'intent'};};
|
|
llm.providerForBot=()=>({route:{provider:'ollama',model:'test'},provider:{decide:async()=>{
|
|
state.revision++;
|
|
state.bots[0].level=6;state.bots[0].zone='New zone';
|
|
state.bots[0].autonomy.observedAt=new Date(now+1000).toISOString();
|
|
if(retire)state.bots[0].status='retired';
|
|
return {action:'explore',goal:'Explore nearby',reason:'Curious'};
|
|
}}});
|
|
assert.equal((await runLiveAutonomyTick(store,{}, {},{now})).ran,true);
|
|
assert.equal(state.bots[0].level,6);assert.equal(state.bots[0].zone,'New zone');
|
|
assert.equal(queued,1);
|
|
retire=true;
|
|
assert.equal((await runLiveAutonomyTick(store,{}, {},{now,forceBotId:'hero'})).ran,false);
|
|
assert.equal(queued,1);
|
|
});
|
|
|
|
test('rest requests never enable indefinite stay',()=>{
|
|
const {compileLivePlayerbotIntent}=require('../.test-build/autonomy.js');
|
|
const source=compileLivePlayerbotIntent('A',{action:'rest',goal:'Recover',reason:'Tired'});
|
|
assert.doesNotMatch(source,/\+stay/);assert.match(source,/-stay/);assert.match(source,/GetHealthPct/);assert.match(source,/AzerothAITrainingOverride/);
|
|
});
|
|
|
|
test('fresh full recovery removes unnecessary rest but stale or low mana does not',()=>{
|
|
const {availableIntentActions,decisionContext}=require('../.test-build/autonomy.js');
|
|
const bot={personality:{activities:['quest','rest']},world:{at:Date.now()/1000,healthPct:100,manaPct:100,questBrief:'A real quest',threatBrief:'Wolf level 7',readinessAt:Date.now()/1000,foodCount:0,drinkCount:0}};
|
|
assert.deepEqual(availableIntentActions(bot),['quest']);assert.match(decisionContext(bot),/A real quest/);assert.match(decisionContext(bot),/Wolf level 7/);
|
|
bot.world.manaPct=20;assert.deepEqual(availableIntentActions(bot),['quest','rest']);
|
|
bot.world.at-=200;assert.deepEqual(availableIntentActions(bot),['quest','rest']);assert.doesNotMatch(decisionContext(bot),/Wolf/);
|
|
});
|
|
test('intent outcomes require fresh matching evidence, not just accepted commands',()=>{
|
|
const {startIntentCheck,observeIntentChecks}=require('../.test-build/intent-evidence.js');
|
|
const now=Date.now()/1000,b={level:10,world:{at:now,x:0,y:0,mapId:0,healthPct:50,manaPct:10,groupNames:[],xp:100},autonomy:{}};
|
|
const check=startIntentCheck(b,{action:'quest'},'q',now);b.autonomy.intentChecks=[check];
|
|
b.world.x=30;b.world.at=now+30;observeIntentChecks(b);assert.equal(check.status,'pending','Walking is not quest progress');
|
|
b.world.xp=110;observeIntentChecks(b);assert.equal(check.status,'observed');
|
|
const rest=startIntentCheck(b,{action:'rest'},'r',now+30);b.autonomy.intentChecks=[rest];b.world.healthPct=100;b.world.manaPct=100;
|
|
observeIntentChecks(b);assert.equal(rest.status,'pending','Same snapshot cannot prove recovery');
|
|
b.world.at=now+60;b.world.inCombat=false;observeIntentChecks(b);assert.equal(rest.status,'observed');
|
|
const party=startIntentCheck(b,{action:'socialize'},'p',now+60,'Peer');b.autonomy.intentChecks=[party];b.world.at=now+90;b.world.nearby=['Peer'];observeIntentChecks(b);assert.equal(party.status,'pending');
|
|
b.world.groupId='g';b.world.groupNames=['Peer'];observeIntentChecks(b);assert.equal(party.status,'observed');
|
|
});
|
|
test('death interrupts intentions and unverified work times out honestly',()=>{
|
|
const {startIntentCheck,observeIntentChecks}=require('../.test-build/intent-evidence.js');const n=Date.now()/1000;
|
|
const b={level:10,world:{at:n,x:0,y:0,mapId:0,groupNames:[],xp:100},autonomy:{}};
|
|
const c=startIntentCheck(b,{action:'trade'},'t',n);b.autonomy.intentChecks=[c];b.world.at=n+601;observeIntentChecks(b);assert.equal(c.status,'no_progress');
|
|
const d=startIntentCheck(b,{action:'quest'},'q',n+601);b.autonomy.intentChecks=[d];b.world.at=n+630;b.world.isGhost=true;observeIntentChecks(b);assert.equal(d.status,'interrupted');
|
|
});
|