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

68 lines
4.2 KiB
TypeScript

/** Pure YAML roster checks. Bundle with the host's yaml dependency and run from the repository root. */
import assert from 'node:assert/strict'
import { readFileSync, readdirSync, existsSync } from 'node:fs'
import { parseConfig, parseFrontmatter, definitionRuns, toolsFor } from './agents.ts'
let count = 0
function test(name: string, run: () => void) { run(); console.log(`ok ${++count} - ${name}`) }
const text=readFileSync('config/config.yaml','utf8')
const config=parseConfig(text)
const fixture=()=>({standing:'worker',prompt:'Shared context',roster:[{name:'worker',description:'Worker',model:'faux/faux-1',thinking:'low',prompt:'Role context',tools:['read']}]})
test('default config contains the full roster and the configured standing model',()=>{
assert.deepEqual(config.definitions.map(d=>d.name),['coordinator','designer','dream-pass','reviewer-architecture','reviewer-correctness','reviewer-simplicity','reviewer','scout','sausage','watcher','worker'])
const standing=config.definitions.find(d=>d.name===config.standing)!
assert.deepEqual(definitionRuns(standing),{model:{provider:'anthropic',modelId:'claude-opus-5-5'},thinking:'high'})
assert(standing.instructions.includes('systemctl restart chat'))
assert(standing.instructions.includes('instead of guessing'))
})
test('global prompt states each harness route, the proc files, and the config path',()=>{
for(const value of ['http://host.sausage/harness/','whoami','conversations','conversations/ID/entries?after=N','events?after=N','models','extensions','credential-requests','/proc/processes','/proc/extensions','jaq','/etc/sausage/config.yaml']) assert(config.prompt.includes(value),value)
})
test('YAML block scalars keep the shared and role prompts separate',()=>{
assert(config.prompt.startsWith('## Sausage and the world'))
assert(config.prompt.includes('\n\n## Inspect the harness state'))
assert(!config.prompt.includes('# Standing agent'))
assert(config.definitions.every(d=>d.instructions.includes('\n')))
})
test('JSON is valid YAML and model references preserve slashes in the model id',()=>{
const value=fixture();value.roster[0].model='faux/org/model'
assert.deepEqual(definitionRuns(parseConfig(JSON.stringify(value)).definitions[0]).model,{provider:'faux',modelId:'org/model'})
})
test('reject missing global prompt, unknown standing name, and duplicate roster names',()=>{
const value=fixture()
assert.throws(()=>parseConfig(JSON.stringify({...value,prompt:null})),/prompt must be a string/)
assert.throws(()=>parseConfig(JSON.stringify({...value,standing:'missing'})),/standing must name a roster member/)
assert.throws(()=>parseConfig(JSON.stringify({...value,roster:[...value.roster,...value.roster]})),/duplicate agent/)
})
test('reject malformed YAML and invalid roster fields',()=>{
assert.throws(()=>parseConfig('roster: [\n'))
for(const patch of [{name:'with space'},{description:''},{prompt:''},{model:'missing-provider'},{thinking:'no'},{tools:'read'},{denyTools:[1]}]) {
const value=fixture();Object.assign(value.roster[0],patch)
assert.throws(()=>parseConfig(JSON.stringify(value)),/config.yaml/)
}
})
test('tool denials also apply to inherited tools without changing the parent',()=>{
const definition=parseConfig(JSON.stringify(fixture())).definitions[0]
const parent=['read','write']
delete definition.tools;definition.addTools=['bash'];definition.denyTools=['write']
assert.deepEqual(toolsFor(definition,parent),['read','bash'])
assert.deepEqual(parent,['read','write'])
})
test('skill frontmatter uses the same YAML parser',()=>{
const parsed=parseFrontmatter('---\r\nname: skill\r\ndescription: >\r\n First line\r\n second line\r\n---\r\nBody')
assert.equal(parsed.frontmatter.name,'skill')
assert.equal(parsed.frontmatter.description,'First line second line\n')
assert.equal(parsed.body,'Body')
})
test('every built-in skill has its matching name, description, and instructions',()=>{
for (const folder of readdirSync('skills')) {
const path=`skills/${folder}/SKILL.md`
if (!existsSync(path)) continue
const {frontmatter,body}=parseFrontmatter(readFileSync(path,'utf8'))
assert.equal(frontmatter.name,folder,path)
assert.equal(typeof frontmatter.description,'string',path)
assert(body.trim(),path)
}
})
console.log(`${count} tests passed`)