feat: port world agents, skills, and pure definition parser

Rewrite Ultimate roles for the WASI world and explicit repositories. Add approved proxy model families, ordered overlays, and executable parser and asset checks.
This commit is contained in:
2026-11-07 00:57:27 +00:00
parent 9cae803323
commit 0dc97c7682
21 changed files with 908 additions and 0 deletions
+148
View File
@@ -0,0 +1,148 @@
/** Node-only test runner. The implementation imports no Node APIs. Run from the repository root. */
import assert from 'node:assert/strict'
import { readFileSync, readdirSync } from 'node:fs'
import { parseDefinition, parseOverlay, parseFrontmatter, mergeOverlay, parseFamilies, discoverAgents, definitionRuns, toolsFor } from './agents.ts'
let count = 0
function test(name: string, run: () => void) { run(); console.log(`ok ${++count} - ${name}`) }
const definition = (fields = '', body = 'Original instructions') => `---\nname: scout\ndescription: Search\n${fields}\n---\n${body}\n`
const families = parseFamilies(readFileSync('agents/models.json', 'utf8'))
const models = [...new Set(Object.values(families).map(family => family.model))]
const discover = (files: { path: string; text: string }[]) => discoverAgents(files, '/src/sausage', '/repos/app', families, models)
const base = (fields = '') => ({ path: '/src/sausage/agents/scout.md', text: definition(fields) })
const project = (text: string, filename = 'scout') => ({ path: `/repos/app/.sausage/agents/${filename}.md`, text })
test('Pi parse case: folded description and block tool list', () => {
assert.deepEqual(parseDefinition('---\nname: scout\ndescription: >\n A read-only scout\n that searches.\nmodel: openai/gpt-6.1-sol\nthinking: max\ntools:\n - read\n - bash\n---\nSearch\n', 'scout.md'), {
name: 'scout', description: 'A read-only scout that searches.', path: 'scout.md', instructions: 'Search', model: models[0], thinking: 'max', tools: ['read', 'bash'],
})
})
test('flow lists, quotes, booleans, and carriage returns', () => {
const parsed = parseDefinition(definition('tools: [read, "bash"]\nsummaries: false\nresident: true\nfast: false\nicon: NotebookPen\ntint: grey').replaceAll('\n', '\r\n'), 'test')
assert.deepEqual(parsed.tools, ['read', 'bash']); assert.equal(parsed.summaries, false)
assert.equal(parsed.resident, true); assert.equal(parsed.fast, false)
assert.equal(parseFrontmatter("---\nname: 'it''s'\n---\nBody").frontmatter.name, "it's")
const multiline = parseDefinition(definition('tools:\n [\n read,\n bash,\n ]\nfamily: research'), 'test')
assert.deepEqual(multiline.tools, ['read', 'bash']); assert.equal(multiline.family, 'research')
assert.deepEqual(parseDefinition(definition('tools: [read, "unknown,tool"]'), 'test').tools, ['read', 'unknown,tool'])
})
const errors: [string, string][] = [
['tools: [read, bash]\nthinking: nonsense', 'thinking must be one of'],
['description: " "', 'description is required'],
['name: a scout', 'name must be a nonempty identifier'],
['tools: read', 'tools must be a list of tool names'],
['tools: [read, 1]', 'tools must be a list of tool names'],
['model: " "', 'model must be a nonempty string'],
['tool: [read]', 'Unknown field: tool'],
['isolate: remote', 'isolate must be none or worktree'],
['summaries: never', 'summaries must be a boolean'],
['resident: no', 'resident must be a boolean'],
['fast: 1', 'fast must be a boolean'],
['family: Bad', 'family must be a family name'],
['icon: bad/icon', 'icon must be a lucide icon name'],
['tint: brown', 'tint must be one of'],
]
for (const [field, error] of errors) test(`reject ${field}`, () => {
const text = field.startsWith('description:') ? definition().replace('description: Search', field) : field.startsWith('name:') ? definition().replace('name: scout', field) : definition(field)
assert.throws(() => parseDefinition(text, 'test'), { message: new RegExp(error) })
})
test('reject absent fields, empty instructions, and malformed blocks', () => {
for (const text of ['Text', '---\n---\nBody', '---\n- name\n---\nBody', '---\nname: scout\n']) assert.throws(() => parseDefinition(text, 'test'), /Expected frontmatter/)
assert.throws(() => parseDefinition(definition().replace('description: Search\n', ''), 'test'), /description is required/)
assert.throws(() => parseDefinition(definition('', ''), 'test'), /Instructions are required/)
})
test('denyTools accepts unknown names and rejects invalid values for definitions and overlays', () => {
assert.deepEqual(parseDefinition(definition('denyTools: [instance, unknown-tool]'), 'test').denyTools, ['instance', 'unknown-tool'])
assert.deepEqual(parseDefinition(definition('denyTools: []'), 'test').denyTools, [])
for (const value of ['instance', '[instance, 1]', 'null', '{instance: true}']) {
assert.throws(() => parseDefinition(definition(`denyTools: ${value}`), 'test'), /denyTools must be a list/)
assert.throws(() => parseOverlay(definition(`addTools: []\ndenyTools: ${value}`), 'test'), /denyTools must be a list/)
}
})
test('ordered overlays add tools and append bodies without changing source', () => {
const found = discover([project('---\nname: scout\naddTools: [write, read]\n---\nFirst', '01'), base('tools: [read, bash]'), project('---\nname: scout\naddTools: [message]\n---\nSecond', '02')])
assert.deepEqual(found.errors, []); assert.deepEqual(found.replaced, [])
const agent = found.definitions[0]
assert.equal(agent.source, 'builtin'); assert.equal(agent.path, base().path)
assert.deepEqual(agent.tools, ['read', 'bash', 'write', 'message'])
assert.equal(agent.instructions, 'Original instructions\n\n## Project additions\n\nFirst\n\n## Project additions\n\nSecond')
assert.deepEqual(agent.overlays, [project('', '01').path, project('', '02').path])
})
test('overlay settings replace while denials accumulate through empty overlay', () => {
const found = discover([base('tools: [read]\ndenyTools: [unknown-tool]\nthinking: low'), project('---\nname: scout\naddTools: []\ndenyTools: [unknown-tool, bash]\nmodel: anthropic/claude-opus-5-5\nthinking: high\nfast: true\ndescription: Our scout\n---', '01'), project('---\nname: scout\naddTools: []\ndenyTools: []\n---', '02')])
assert.deepEqual(found.errors, [])
assert.deepEqual(found.definitions[0].denyTools, ['unknown-tool', 'bash'])
assert.equal(found.definitions[0].thinking, 'high'); assert.equal(found.definitions[0].fast, true)
assert.equal(found.definitions[0].description, 'Our scout')
assert.deepEqual(found.definitions[0].tools, ['read'])
})
test('inherited tools retain deferred additions and do not mutate parent', () => {
const parent = ['read', 'bash', 'write', 'subagent']
const child = mergeOverlay(parseDefinition(definition('denyTools: [write, subagent]'), 'test'), parseOverlay('---\nname: scout\naddTools: [message]\n---', 'overlay'))
assert.equal(child.tools, undefined); assert.deepEqual(child.addTools, ['message'])
assert.deepEqual(toolsFor(child, parent), ['read', 'bash', 'message'])
assert.deepEqual(toolsFor(parseDefinition(definition('tools: [read, write]\ndenyTools: [write]'), 'test'), parent), ['read'])
assert.deepEqual(parent, ['read', 'bash', 'write', 'subagent'])
})
test('overlay errors do not replace a valid base', () => {
const found = discover([base(), project('---\nname: scout\naddTools: [x]\ntools: [read]\n---', 'listed'), project('---\nname: scout\naddTools: []\ncolour: red\n---', 'unknown'), project('---\nname: nobody\naddTools: [x]\n---', 'orphan')])
assert.deepEqual(Object.fromEntries(found.errors.map(error => [error.name, error.error])), {
listed: 'Error: An overlay adds tools with addTools, not tools', unknown: 'Error: Unknown field: colour', orphan: 'Error: addTools needs an earlier definition named nobody',
})
assert.equal(found.definitions[0].instructions, 'Original instructions')
assert.throws(() => parseOverlay('---\nname: scout\naddTools: read\n---', 'test'), /addTools must be a list/)
})
test('replacement uses name, not filename, and respects exact roots', () => {
const found = discover([base(), project(definition('family: research', 'Replacement'), 'different'), { path: '/repos/other/.sausage/agents/scout.md', text: definition() }, { path: '/src/sausage/agents/nested/no.md', text: definition() }])
assert.deepEqual(found.errors, []); assert.equal(found.definitions.length, 1)
assert.equal(found.definitions[0].source, 'project'); assert.equal(found.definitions[0].instructions, 'Replacement')
assert.deepEqual(found.replaced, [{ name: 'scout', path: project('', 'different').path, source: 'project' }])
})
test('family fallback, thinking override, model suffix, and unknown models', () => {
const run = (fields: string) => definitionRuns(parseDefinition(definition(fields), 'test'), families, models)
assert.equal(run('family: missing').family, 'default')
assert.equal(run('family: constructor').family, 'default')
assert.equal(run('family: research').thinking, 'high')
assert.equal(run('family: research\nthinking: low').thinking, 'low')
assert.equal(run('model: openai/gpt-6.1-sol').thinking, undefined)
assert.equal(run('model: openai/gpt-6.1-sol:high').thinking, 'high')
assert.equal(run('model: openai/gpt-6.1-sol:high\nthinking: low').thinking, 'low')
for (const model of ['anthropic/unknown', 'unknown']) assert.match(discover([base(`model: ${model}`)]).errors[0].error, /model: Model .* not found/)
const found = discover([base('model: openai/gpt-6.1-sol'), project('---\nname: scout\nfamily: lead\naddTools: []\n---')])
assert.equal(found.definitions[0].model, undefined); assert.equal(found.definitions[0].runs.family, 'lead')
const suffix = discover([base('thinking: low'), project('---\nname: scout\nmodel: openai/gpt-6.1-sol:high\naddTools: []\n---')])
assert.equal(suffix.definitions[0].runs.thinking, 'high')
})
test('family world file requires valid refs and default', () => {
for (const text of ['null', '[]', '{}', '{"default":{"model":"bad"}}', '{"Default":{"model":"openai/a"}}', '{"default":{"model":"openai/a","thinking":"bad"}}']) assert.throws(() => parseFamilies(text))
assert.deepEqual(Object.keys(families), ['default', 'fast', 'review', 'research', 'design', 'lead'])
assert.equal(families.review.thinking, 'xhigh')
assert.equal(families.design.thinking, 'high')
})
test('all shipped definitions and overlays parse and discover', () => {
const files = readdirSync('agents').filter(name => name.endsWith('.md')).map(name => ({ path: `/src/sausage/agents/${name}`, text: readFileSync(`agents/${name}`, 'utf8') }))
assert.equal(files.length, 12)
const available = ['read', 'bash', 'edit', 'write', 'message', 'note', 'notify', 'automation', 'branch', 'compact', 'remember', 'memory', 'todo', 'goal', 'subagent', 'plan', 'workspace']
for (const file of files) {
const agent = 'addTools' in parseFrontmatter(file.text).frontmatter ? parseOverlay(file.text, file.path) : parseDefinition(file.text, file.path)
assert.ok(!/\b(pnpm|vitest|clone|instance|restart|web_search|mcp|setup|codemode|debug)\b|~\/\.ultimate/.test(agent.instructions), file.path)
const fields = 'settings' in agent ? agent.settings : agent
for (const tool of [...(fields.tools ?? []), ...(fields.denyTools ?? []), ...('addTools' in agent ? agent.addTools ?? [] : [])]) assert.ok(available.includes(tool), `${file.path}: ${tool}`)
}
const found = discover(files)
assert.deepEqual(found.errors, []); assert.equal(found.definitions.length, 11)
assert.equal(found.definitions.find(agent => agent.name === 'coordinator')!.runs.family, 'lead')
for (const agent of found.definitions.filter(agent => agent.name.startsWith('reviewer'))) assert.equal(agent.runs.thinking, 'xhigh')
})
test('all applicable skills parse and avoid unavailable tools', () => {
let skills = 0
for (const name of readdirSync('skills')) {
if (name === 'README.md') continue
const parsed = parseFrontmatter(readFileSync(`skills/${name}/SKILL.md`, 'utf8'))
assert.equal(parsed.frontmatter.name, name)
assert.equal(typeof parsed.frontmatter.description, 'string'); assert.ok(parsed.body)
assert.ok(!/\b(pnpm|vitest|clone|instance|restart|web_search|mcp|setup|codemode|debug)\b|~\/\.ultimate/.test(parsed.body), name)
skills++
}
assert.equal(skills, 4)
})
console.log(`${count} checks passed`)
+171
View File
@@ -0,0 +1,171 @@
/** Pure parsing and ordered overlays for world agent files. The caller supplies all file contents and model configuration. */
export const thinkingLevels = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'] as const
export type Thinking = typeof thinkingLevels[number]
export type Family = { model: string; thinking?: Thinking }
export type Families = Record<string, Family>
export interface Definition {
name: string
description: string
path: string
instructions: string
model?: string
thinking?: Thinking
family?: string
tools?: string[]
addTools?: string[]
denyTools?: string[]
isolate?: 'none' | 'worktree'
resident?: boolean
fast?: boolean
summaries?: boolean
icon?: string
tint?: string
overlays?: string[]
}
export type File = { path: string; text: string }
const fields = ['name', 'description', 'model', 'thinking', 'family', 'tools', 'denyTools', 'isolate', 'resident', 'fast', 'summaries', 'icon', 'tint']
const familyName = (name: string) => /^[a-z][a-z0-9-]{0,23}$/.test(name)
const tools = (value: unknown): value is string[] => Array.isArray(value) && value.every(v => typeof v === 'string')
/** Scalars and flow lists, plus the block lists and folded description used in Ultimate's parser tests. Not general YAML. */
function scalar(input: string): unknown {
const text = input.trim()
if (text.startsWith('[')) {
if (!text.endsWith(']')) throw new Error('Unclosed flow list')
const items = text.slice(1, -1).match(/\s*(?:"(?:\\.|[^"\\])*"|'(?:''|[^'])*'|[^,]+)/g) ?? []
return items.filter(item => item.trim()).map(item => scalar(item))
}
if (text.startsWith('"')) return JSON.parse(text)
if (text.startsWith("'")) {
if (!text.endsWith("'")) throw new Error('Unclosed scalar')
return text.slice(1, -1).replaceAll("''", "'")
}
if (text === 'true') return true
if (text === 'false') return false
if (text === 'null' || text === '~' || text === '') return null
if (/^-?\d+(\.\d+)?$/.test(text)) return Number(text)
if (text.startsWith('{')) return {} // Mapping values cannot satisfy any supported field.
return text.replace(/\s+#.*$/, '')
}
export function parseFrontmatter(text: string): { frontmatter: Record<string, unknown>; body: string } {
const lines = text.replaceAll('\r\n', '\n').split('\n')
const end = lines.indexOf('---', 1)
if (lines[0] !== '---' || end < 0) throw new Error('Expected frontmatter fields between --- lines')
const frontmatter: Record<string, unknown> = {}
for (let i = 1; i < end; i++) {
if (!lines[i].trim() || lines[i].trimStart().startsWith('#')) continue
const match = /^([A-Za-z][A-Za-z0-9]*):\s*(.*)$/.exec(lines[i])
if (!match) throw new Error('Expected frontmatter fields between --- lines')
const [, key, raw] = match
let value = raw
if (value.trim().startsWith('[') && !value.trim().endsWith(']')) {
while (i + 1 < end && !value.trim().endsWith(']')) value += ' ' + lines[++i].trim()
}
if (raw === '>' || raw === '|' || raw === '') {
const block: string[] = []
while (i + 1 < end && /^\s+\S/.test(lines[i + 1])) block.push(lines[++i].trim())
if (raw === '>') frontmatter[key] = block.join(' ')
else if (raw === '|') frontmatter[key] = block.join('\n')
else frontmatter[key] = block[0]?.startsWith('[') ? scalar(block.join(' ')) : block.length ? block.map(line => scalar(line.replace(/^-\s+/, ''))) : null
} else frontmatter[key] = scalar(value)
}
if (!Object.keys(frontmatter).length) throw new Error('Expected frontmatter fields between --- lines')
return { frontmatter, body: lines.slice(end + 1).join('\n').trim() }
}
function settings(data: Record<string, unknown>, overlay: boolean) {
const unknown = Object.keys(data).find(key => !fields.includes(key) && !(overlay && key === 'addTools'))
if (unknown) throw new Error(`Unknown field: ${unknown}`)
if (typeof data.name !== 'string' || !/^[a-zA-Z0-9_-]+$/.test(data.name)) throw new Error('name must be a nonempty identifier')
for (const key of ['description', 'model']) {
if (data[key] !== undefined && (typeof data[key] !== 'string' || !data[key].trim())) throw new Error(key === 'description' ? 'description is required' : 'model must be a nonempty string')
}
if (data.family !== undefined && (typeof data.family !== 'string' || !familyName(data.family))) throw new Error('family must be a family name: lowercase letters, digits and -')
if (data.thinking !== undefined && !thinkingLevels.includes(data.thinking as Thinking)) throw new Error(`thinking must be one of ${thinkingLevels.join(', ')}`)
for (const key of ['tools', 'denyTools', 'addTools']) {
if (data[key] !== undefined && !tools(data[key])) throw new Error(`${key} must be a list of tool names`)
}
if (data.isolate !== undefined && data.isolate !== 'none' && data.isolate !== 'worktree') throw new Error('isolate must be none or worktree')
for (const key of ['resident', 'fast', 'summaries']) {
if (data[key] !== undefined && typeof data[key] !== 'boolean') throw new Error(`${key} must be a boolean`)
}
if (data.icon !== undefined && (typeof data.icon !== 'string' || !/^[A-Za-z][A-Za-z0-9]*(-[a-z0-9]+)*$/.test(data.icon))) throw new Error('icon must be a lucide icon name, like Gem or notebook-pen')
if (data.tint !== undefined && !['violet', 'blue', 'green', 'amber', 'red', 'pink', 'teal', 'grey'].includes(data.tint as string)) throw new Error('tint must be one of violet, blue, green, amber, red, pink, teal, grey')
return { ...data, ...(typeof data.description === 'string' ? { description: data.description.trim() } : {}) }
}
export function parseDefinition(text: string, path: string): Definition {
const { frontmatter, body } = parseFrontmatter(text)
const parsed = settings(frontmatter, false)
if (!parsed.description) throw new Error('description is required')
if (!body) throw new Error('Instructions are required')
return { ...parsed, path, instructions: body } as unknown as Definition
}
export function parseOverlay(text: string, path: string) {
const { frontmatter, body } = parseFrontmatter(text)
if ('tools' in frontmatter) throw new Error('An overlay adds tools with addTools, not tools')
const parsed = settings(frontmatter, true)
if (!tools(parsed.addTools)) throw new Error('addTools must be a list of tool names')
const { name, addTools, ...rest } = parsed
return { name: name as string, addTools, settings: rest as Partial<Definition>, path, instructions: body }
}
export function mergeOverlay(base: Definition, overlay: ReturnType<typeof parseOverlay>): Definition {
const { model: _pin, ...unpinned } = base
const suffix = overlay.settings.model?.match(/:(off|minimal|low|medium|high|xhigh|max)$/)
const resolved = suffix ? { ...overlay.settings, model: overlay.settings.model!.slice(0, -suffix[0].length), thinking: overlay.settings.thinking ?? suffix[1] as Thinking } : overlay.settings
return {
...(overlay.settings.family && !overlay.settings.model ? unpinned : base),
...resolved,
...(overlay.settings.denyTools ? { denyTools: [...new Set([...(base.denyTools ?? []), ...overlay.settings.denyTools])] } : {}),
...(base.tools ? { tools: [...new Set([...base.tools, ...overlay.addTools])] } : overlay.addTools.length ? { addTools: [...new Set([...(base.addTools ?? []), ...overlay.addTools])] } : {}),
instructions: overlay.instructions ? `${base.instructions}\n\n## Project additions\n\n${overlay.instructions}` : base.instructions,
overlays: [...(base.overlays ?? []), overlay.path],
}
}
export function parseFamilies(text: string): Families {
const value = JSON.parse(text)
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('families must be an object')
for (const [name, spec] of Object.entries(value) as [string, Family][]) {
if (!familyName(name) || !spec || typeof spec !== 'object' || !modelRef(spec.model)) throw new Error(`Invalid family: ${name}`)
if (spec.thinking !== undefined && !thinkingLevels.includes(spec.thinking)) throw new Error(`Invalid thinking for family: ${name}`)
}
if (!value.default) throw new Error('default family is required')
return value
}
function modelRef(model: unknown): model is string { return typeof model === 'string' && /^[^/\s]+\/\S+$/.test(model) }
export function definitionRuns(definition: Definition, families: Families, models: readonly string[]) {
const family = Object.hasOwn(families, definition.family ?? 'default') ? definition.family ?? 'default' : 'default'
let model = definition.model ?? families[family].model
let thinking = definition.thinking ?? (definition.model ? undefined : families[family].thinking)
const suffix = /:(off|minimal|low|medium|high|xhigh|max)$/.exec(model)
if (suffix) { model = model.slice(0, -suffix[0].length); thinking = definition.thinking ?? suffix[1] as Thinking }
if (!modelRef(model) || !models.includes(model)) throw new Error(`model: Model "${model}" not found`)
const slash = model.indexOf('/')
return { model: { provider: model.slice(0, slash), modelId: model.slice(slash + 1) }, thinking, ...(definition.model ? { via: 'pin' as const } : { via: 'family' as const, family }) }
}
/** Sorted built-ins precede sorted project files. Invalid files produce errors without replacing an earlier definition. */
export function discoverAgents(files: readonly File[], builtinRoot: string, repo: string, families: Families, models: readonly string[]) {
const definitions = new Map<string, Definition & { source: 'builtin' | 'project' }>()
const errors: { name: string; path: string; source: string; error: string }[] = []
const replaced: { name: string; path: string; source: string }[] = []
for (const [source, root] of [['builtin', builtinRoot + '/agents/'], ['project', repo + '/.sausage/agents/']] as const) {
for (const file of files.filter(file => file.path.startsWith(root) && !file.path.slice(root.length).includes('/') && file.path.endsWith('.md')).toSorted((a, b) => a.path.localeCompare(b.path))) {
try {
let definition: Definition
if ('addTools' in parseFrontmatter(file.text).frontmatter) {
const overlay = parseOverlay(file.text, file.path)
const base = definitions.get(overlay.name)
if (!base) throw new Error(`addTools needs an earlier definition named ${overlay.name}`)
definition = mergeOverlay(base, overlay)
} else definition = parseDefinition(file.text, file.path)
definitionRuns(definition, families, models)
const previous = definitions.get(definition.name)
if (source === 'project' && previous?.source === 'builtin' && !definition.overlays) replaced.push({ name: definition.name, path: file.path, source })
definitions.set(definition.name, { ...definition, source: definition.overlays ? previous!.source : source })
} catch (error) { errors.push({ name: file.path.split('/').at(-1)!.slice(0, -3), path: file.path, source, error: String(error) }) }
}
}
return { definitions: [...definitions.values()].map(definition => ({ ...definition, runs: definitionRuns(definition, families, models) })), errors, replaced }
}
/** Tool denials also apply to inherited tools. Inputs stay unchanged. */
export function toolsFor(definition: Definition, parent: readonly string[]): string[] {
return [...new Set([...(definition.tools ?? parent), ...(definition.addTools ?? [])])].filter(tool => !definition.denyTools?.includes(tool))
}