import assert from 'node:assert/strict' import { readFileSync, readdirSync } from 'node:fs' import { dirname, join } from 'node:path' import { test } from 'node:test' import { fileURLToPath } from 'node:url' import { facetContextText, facetRuleText } from '../lib/scope.js' const PRESETS = join(dirname(fileURLToPath(import.meta.url)), '..', 'presets') /** * The prompt variables the harness registers for a section. * * `dsh-agent-loop` puts each of these into the prompt registry, and an assembly * substitutes only what is registered. An unknown reference is not rendered as * text — interpolation throws, and the throw takes the entire turn with it, so * the user sees "This turn failed" and nothing else. A harness release that * stopped registering `cwd` is exactly how this bundle's preset stopped * composing sessions: the persona suffix it copied from the deployment said * "Your working directory is …" with that variable in it. */ const REGISTERED = ['provider', 'model'] /** Every `{{name}}` reference in one text, as the name alone. */ function referencesIn(text) { return [...text.matchAll(/\{\{([^{}]*)\}\}/g)].map((match) => match[1]) } test('no preset references a prompt variable the harness does not register', () => { const files = readdirSync(PRESETS).filter((name) => name.endsWith('.yml')) assert.ok(files.length > 0, 'the bundle ships at least one preset') const found = [] for (const file of files) { for (const name of referencesIn(readFileSync(join(PRESETS, file), 'utf8'))) found.push([file, name]) } // The model reference is the one that has to keep working, so the guard cannot // be satisfied by a preset that references nothing at all. assert.ok( found.some(([, name]) => name === 'model'), 'a preset still names the model', ) for (const [file, name] of found) { assert.ok( REGISTERED.includes(name), `${file} references {{${name}}}; the harness registers only ${REGISTERED.join(', ')}, and an unregistered reference fails the whole turn`, ) } }) test('the text this bundle contributes to the prompt holds no variable references', () => { // The rule and the binding are rebuilt from real values on every request, so a // placeholder in either would be a reference the harness never resolves — the // same failure, this time from the bundle's own hand. const rule = facetRuleText() const binding = facetContextText({ cwd: '/w', root: '/w/src', extras: ['/w/shared'], mode: 'ask' }) assert.ok(rule.length > 0 && binding.length > 0, 'both halves contribute text') assert.deepEqual(referencesIn(rule), []) assert.deepEqual(referencesIn(binding), []) })