66 lines
4.4 KiB
JavaScript
66 lines
4.4 KiB
JavaScript
// C2-06: real broker, real Git reads and fixture-only library. Reused by runner acceptance tests.
|
|
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import { Store } from '../src/store.mjs';
|
|
import { Broker } from '../src/broker.mjs';
|
|
import { checkReply, GuardError, libraryReader } from '../../library/src/guard.mjs';
|
|
import { THREAD_DEFINITIONS } from '../../organization/src/vocabulary.mjs';
|
|
import { resolveReplyLimits } from '../../library/src/limits.mjs';
|
|
import { mapping, repo, MARKER } from '../../library/tests/helpers.mjs';
|
|
|
|
export const citedBody = citations => JSON.stringify({ text: 'A fixture answer.', citations });
|
|
export const fixtureCite = (commit, extra = {}) => ({ path: 'Book One/Characters/Mara Vell.md', commit, lines: [9, 9], quote: 'Grey eyes.', ...extra });
|
|
|
|
export function citationWorld(t, { definition = 'coder', library = true, guard: overrideGuard, organization = null, sendAuthority = true, mapChange } = {}) {
|
|
const r = repo(t), root = r.root, state = join(root, 'bus'); mkdirSync(state, { mode: 0o700 });
|
|
const store = new Store(state); t.after(() => store.close());
|
|
const map = mapping(mapChange), settings = join(root, 'business.json');
|
|
writeFileSync(settings, JSON.stringify({ replyLimits: { requireCitation: false, citationsPerReply: 20 } }));
|
|
const business = { id: 'acme', human: 'jason', vars: { 'bus.tester': true },
|
|
...(library ? { projects: { books: map } } : {}),
|
|
arbiters: { technical: 'chief', delivery: 'chief' },
|
|
roles: {chief: { definition: 'chief-of-staff', authority: { withinRole: ['decision.resolve.technical', 'decision.resolve.delivery'], crossRole: [] } }, coder: { definition, web: true, authority: { withinRole: sendAuthority ? ['message.send'] : [], crossRole: [] } } } };
|
|
const limits = () => {
|
|
try { return resolveReplyLimits({ readable: true, document: JSON.parse(readFileSync(settings, 'utf8')) }); }
|
|
catch { return resolveReplyLimits({ readable: false }); }
|
|
};
|
|
let served = r.commit;
|
|
const reads = [];
|
|
const adapter = {
|
|
GuardError,
|
|
replyLimits: limits,
|
|
guard: overrideGuard ?? ((context, body, opts) => checkReply(body, {
|
|
limits: opts.limits, mapping: map, library: libraryReader(r.repo, served, map),
|
|
web: { allowed: ['companion', 'research'].includes(opts.definition), pages: opts.pages },
|
|
})),
|
|
last: async (context, paths) => {
|
|
const out = new Map(); for (const path of new Set(paths)) { reads.push(path); out.set(path, await libraryReader(r.repo, served, map).last(path)); } return out;
|
|
},
|
|
readFile: async () => ({}), status: async () => ({ project: 'books', served }),
|
|
};
|
|
const broker = new Broker({ store, businesses: { acme: business }, library: adapter, organization });
|
|
const human = broker.bindHuman({ business: 'acme', human: 'jason', via: 'cli', outsideAgent: true });
|
|
const project = THREAD_DEFINITIONS.includes(definition)
|
|
? broker.request(human, { verb: 'project.create', args: { name: 'Citation fixture' } }) : null;
|
|
const thread = project
|
|
? broker.request(human, { verb: 'thread.create', args: { project: project.id, name: 'Fixture thread', instance: 'coder' } }) : null;
|
|
const launch = { business: 'acme', role: 'coder', run: 'coder-run', harness: 'pi', ...(thread ? { thread: thread.thread ?? thread.id } : {}) };
|
|
const agent = broker.bindLaunch(launch);
|
|
const tester = broker.bindTester({ business: 'acme' });
|
|
const call = (cap, verb, args = {}) => broker.request(cap, { verb, args });
|
|
return { ...r, root, store, business, broker, agent, human, tester, settings, limits, call, adapter, reads, launch, thread, setServed: commit => { served = commit; } };
|
|
}
|
|
|
|
export function guardCases(commit) {
|
|
const good = fixtureCite(commit);
|
|
return [
|
|
['reply-invalid', JSON.stringify({ text: 'x', citations: [], invented: true })],
|
|
['citation-missing', 'Uncited fixture answer.'],
|
|
['citations-over-limit', citedBody(Array(21).fill(good))],
|
|
['citation-invalid', citedBody([{ ...good, quote: 'wrong fixture quote' }])],
|
|
['playground-cited-as-canon', citedBody([{ ...good, path: 'Book One/Playground/What If.md', lines: [4, 4], quote: MARKER, as: 'canon' }])],
|
|
['citation-cross-universe', citedBody([good, { ...good, path: 'Second World/Chapters/01.md', lines: [1, 1], quote: MARKER }])],
|
|
['citation-unfetched', citedBody([{ url: 'https://fixture.invalid/page', quote: 'no fetch' }])],
|
|
];
|
|
}
|