Files
sausage/extensions/core/agents.test.ts
T

57 lines
4.2 KiB
TypeScript

/** Pure checks for world settings, Markdown definitions, overlays, and skill frontmatter. */
import assert from 'node:assert/strict'
import {readFileSync,readdirSync} from 'node:fs'
import {parseConfig,parseDefinition,parseFrontmatter,definitionRuns,toolsFor} from './agents.ts'
let count=0
function test(name:string,run:()=>void){run();console.log(`ok ${++count} - ${name}`)}
const config=parseConfig(readFileSync('config/config.yaml','utf8'))
const definitions=readdirSync('config/roster').sort().map(name=>parseDefinition(readFileSync(`config/roster/${name}`,'utf8'),`config/roster/${name}`))
test('exactly eight built-in Markdown definitions and no inline roster',()=>{
assert.deepEqual(definitions.map(d=>d.name),['coordinator','designer','dream-pass','reviewer','sausage','scout','watcher','worker'])
assert.equal(config.roster,undefined);assert.equal(config.main_agent_name,'sausage')
for(const d of definitions){
const model=['coordinator','designer','sausage'].includes(d.name)?'anthropic/claude-opus-5-5':d.name==='reviewer'?'openai/gpt-6-astra':['scout','watcher'].includes(d.name)?'openai/gpt-6-luna':'openai/gpt-6.1-sol'
assert.equal(d.model,model)
assert.equal(d.thinking,['scout','watcher'].includes(d.name)?'low':['worker','dream-pass'].includes(d.name)?'medium':'high')
}
})
test('shared prompt contains state inspection and current world paths',()=>{
for(const text of ['http://host.sausage/harness/','whoami','conversations/ID/entries?after=N','events?after=N','credential-requests','/proc/processes','/proc/extensions','jaq','/etc/sausage/config.yaml'])assert(config.prompt.includes(text),text)
})
test('main role describes direct self-change and state inspection',()=>{
const role=definitions.find(d=>d.name===config.main_agent_name)!
assert(role.instructions.includes('Act directly'));assert(role.instructions.includes('instead of guessing'))
})
test('filename defines the name and model IDs preserve slashes',()=>{
const d=parseDefinition('---\ndescription: Example\nmodel: local/org/model\n---\nRole','/roster/new-name.md')
assert.equal(d.name,'new-name');assert.deepEqual(definitionRuns(d).model,{provider:'local',modelId:'org/model'})
})
test('an additive project overlay retains base settings and appends instructions',()=>{
const base=parseDefinition('---\ndescription: Base\nmodel: local/model\ntools: [read]\n---\nBase instructions','/roster/worker.md')
const overlay=parseDefinition('---\naddTools: [bash]\ndenyTools: [read]\n---\nProject instructions','/project/.sausage/roster/worker.md',base)
assert.equal(overlay.model,'local/model');assert.deepEqual(toolsFor(overlay,[]),['bash'])
assert(overlay.instructions.includes('Base instructions'));assert(overlay.instructions.includes('Project instructions'))
})
test('a complete project definition replaces the body',()=>{
const base=definitions.find(d=>d.name==='worker')!
const next=parseDefinition('---\ndescription: Project worker\nmodel: local/model\n---\nReplacement','/project/.sausage/roster/worker.md',base)
assert.equal(next.instructions,'Replacement')
})
test('missing settings and malformed definition fields fail with a path',()=>{
assert.throws(()=>parseConfig('main_agent_name: sausage'),/prompt/)
assert.throws(()=>parseConfig('prompt: text'),/main_agent_name/)
assert.throws(()=>parseDefinition('---\nmodel: local/model\n---\nRole','/roster/example.md'),/description/)
assert.throws(()=>parseDefinition('---\ndescription: Example\nmodel: invalid\n---\nRole','/roster/example.md'),/example.md/)
assert.throws(()=>parseDefinition('---\ndescription: Example\nmodel: local/model\nthinking: invalid\n---\nRole','/roster/example.md'),/thinking/)
})
test('tool denials do not mutate inherited tools',()=>{
const inherited=['read','bash'];assert.deepEqual(toolsFor({...definitions[0],denyTools:['bash']},inherited),['read']);assert.deepEqual(inherited,['read','bash'])
})
test('all built-in skills contain valid YAML frontmatter',()=>{
for(const name of readdirSync('skills',{withFileTypes:true}).filter(e=>e.isDirectory()).map(e=>e.name)){
const skill=parseFrontmatter(readFileSync(`skills/${name}/SKILL.md`,'utf8'))
assert.equal(skill.frontmatter.name,name);assert.equal(typeof skill.frontmatter.description,'string');assert(skill.body.length>0)
}
})
console.log(`${count} tests passed`)