// C2-14 fixture: the real host/launcher with C2-06's scripted citation adapter. No model binary runs. import { chmodSync, mkdirSync, writeFileSync, existsSync, readdirSync } from 'node:fs'; import { join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { BusError, writeGrantEvent } from '../../packages/bus/src/broker.mjs'; import { Store } from '../../packages/bus/src/store.mjs'; import { startBroker } from '../../packages/bus/src/runtime.mjs'; import { grantDecision } from '../../packages/bus/tests/host-fixtures.mjs'; import { launchDoc } from '../../packages/organization/tests/helpers.mjs'; import { fakeHarnesses, fixture } from '../../packages/cli/tests/helpers.mjs'; import { bootConfig, loadSystem } from '../../packages/cli/src/config.mjs'; import { createLauncher } from '../../packages/cli/src/launcher.mjs'; import { SESSION_CREDENTIALS } from '../../packages/seat/src/session.mjs'; import * as library from '../../packages/library/src/index.mjs'; import { FAKE_ZAI_KEY } from './fixture-credentials.mjs'; const ADAPTER = fileURLToPath(new URL('./citation-adapter.mjs', import.meta.url)); const REPLY_ADAPTER = fileURLToPath(new URL('../../packages/harness/tests/fixtures/citation-adapter.mjs', import.meta.url)); const ROTATE = '2099-01-01'; const AT = '2026-10-24T00:00:00.000Z'; const shell = value => "'" + String(value).replaceAll("'", "'\\''") + "'"; // A fixture grant is part of the brief. It uses the normal store shape; no executor/network mint or // role-policy modification occurs. This is deliberately distinct from the row287 grant acceptance. function seedGrant(dataRoot, storeRoot, token) { const dir = join(storeRoot, 'acme', 'bean'); mkdirSync(dir, { recursive: true, mode: 0o700 }); writeFileSync(join(dir, 'gitea'), token + '\n', { mode: 0o600 }); const store = new Store(dataRoot); try { const decision = grantDecision(store, { business: 'acme', grant: { method: 'gitea', agent: 'bean', target: { projects: ['books'] }, rotateBy: ROTATE, } }); store.run('INSERT INTO gitea_users(business,agent,user,id,nonce,decision) VALUES(?,?,?,?,?,?)', 'acme', 'bean', 'acme-bean', 1, '2270227022702270', decision); store.run('INSERT INTO gitea_grants(business,agent,user,token_id,token_name,projects,rotate_by,decision) VALUES(?,?,?,?,?,?,?,?)', 'acme', 'bean', 'acme-bean', 1, 'fixture-227', '["books"]', ROTATE, decision); writeGrantEvent(store, AT, 'acme', { operation: 'grant.gitea', decision }, decision); writeGrantEvent(store, AT, 'acme', { action: 'grant.applied', method: 'gitea', agent: 'bean', grantDecision: decision, user: 'acme-bean', userId: 1, tokenId: 1, forks: [], repositories: [{ project: 'books', repository: 'fixture', level: 'write' }] }, 'bean'); } finally { store.close(); } } export async function launchFixture(root, entry, { fetchToken, seatToken, humanToken, ca }) { const configDir = join(root, 'config'), storeRoot = join(configDir, 'secrets', 'agents'); const slotDir = join(configDir, 'secrets', 'acme'); mkdirSync(slotDir, { recursive: true, mode: 0o700 }); for (const [slot, token] of [[library.SLOT, fetchToken], [library.HUMAN_SLOT, humanToken]]) writeFileSync(join(slotDir, slot), token + '\n', { mode: 0o600 }); const plan = join(root, 'citation-plan.json'); writeFileSync(plan, '[]\n', { mode: 0o600 }); const f = fixture(root, 'acme', doc => { // The unused factory roles remain valid, but no tracker is configured or polled. delete doc.tracker; doc.projects = { books: entry }; doc.roles.chief.credentials = { gitea: { file: join(slotDir, library.SLOT), rotateBy: ROTATE } }; doc.roles.bean = { definition: 'companion', credentials: { gitea: { store: true, rotateBy: ROTATE } }, identity: { name: 'Bean fixture', email: 'bean@example.test', gitea: { url: new URL(entry.remote).origin, user: 'acme-bean' } }, publish: ['books'], vars: { harness: 'pi', model: 'claude-sonnet-5-5', thinking: 'high' } }; doc.launch = { ...launchDoc('chief', ['bean'], { opus: 1, sonnet: 1 }), threadIdleSeconds: 3600 }; doc.plugins = { ...doc.plugins, channel: ['webui'] }; doc.replyLimits = { requireCitation: true, citationsPerReply: 20 }; return doc; }); chmodSync(configDir, 0o700); for (const key of SESSION_CREDENTIALS.flat()) delete f.env[key]; // Joined P3 keeps its real preflight; this fake route owns only its made-up ZAI key. for (const key of ['OPENAI_API_KEY', 'OPENROUTER_API_KEY', 'ZAI_API_KEY']) delete f.env[key]; f.env.ZAI_API_KEY = FAKE_ZAI_KEY; seedGrant(f.dataRoot, storeRoot, seatToken); const system = loadSystem({ env: f.env }); const { trackers, ...boot } = bootConfig({ system, businessId: 'acme', env: f.env }); if (trackers !== undefined) throw new Error('acceptance fixture must not configure tracker polling'); const command = (kind, url, opts) => { const c = library.syncCommand(kind, url, opts); return { argv: c.argv, env: { ...c.env, GIT_SSL_CAINFO: ca } }; }; const runtime = await startBroker({ ...boot, librarySync: { command, intervalMs: 3_600_000, configDir } }); const hooks = [], cleanups = [], caps = new Map(), records = [], logs = []; let closing = null; const close = () => (closing ??= (async () => { try { for (const hook of hooks) await hook(0); } finally { try { await runtime.close(); } finally { for (const cleanup of cleanups.reverse()) await cleanup(); const directory = join(f.dataRoot, 'control', 'runtimes'); const remaining = existsSync(directory) ? readdirSync(directory) : []; if (remaining.length) throw new Error('owned-runtime-cleanup-incomplete'); } } })()); try { const ops = { identity: m => runtime.identity(m.cap), authorizeLaunch: m => runtime.authorizeLaunch(m.cap, m.instance), refuse: m => runtime.refuse(m.cap, m.code, m.subject), endLaunch: m => runtime.endLaunch(m.record), credentialStatus: m => runtime.credentialStatus(m.business, m.role), threadsState: m => runtime.threadsState(m.business), threadIdle: m => runtime.threadIdle(m.record), mayLaunch: m => runtime.mayLaunch(m), landHeld: m => runtime.landHeld(m.record), landHolders: m => runtime.landHolders(m.business), landRelease: m => runtime.landRelease(m.record), sessionClears: m => runtime.sessionClears(m.business), lifecycleRequests: m => runtime.lifecycleRequests(m.business), }; const wrap = fn => async value => { try { return await fn(value); } catch (e) { throw Object.assign(new Error('fixture host refused'), { code: e instanceof BusError || /^[a-z-]+$/.test(e?.code ?? '') ? e.code : 'fixture-host-refused' }); } }; const host = { business: 'acme', path: runtime.path, dataRoot: f.dataRoot, op: wrap(m => { if (!Object.hasOwn(ops, m.op)) throw new BusError('invalid-host-request'); return ops[m.op](m); }), bindLaunch: wrap(async record => { const bound = runtime.bindLaunch(record); records.push(record); caps.set(record.thread, bound.cap); return bound; }), beforeClose: fn => void hooks.push(fn), }; const adapter = join(root, 'citation-adapter.sh'); writeFileSync(adapter, `cp ${shell(plan)} "$(dirname "$MOSAIC_TURN_MARKER")/fixture-steps.json"\nexec ${shell(process.execPath)} ${shell(ADAPTER)}\n`, { mode: 0o644 }); const launcher = createLauncher({ host, system, env: f.env, makeLoader: await fakeHarnesses({ after: fn => cleanups.push(fn) }, { pi: adapter }, write => write('channel', 'webui', `export * from ${JSON.stringify(new URL('../../packages/plugins/channel/webui/index.mjs', import.meta.url).href)};`)), readOnly: [ADAPTER, REPLY_ADAPTER, plan], harnessVersion: '0.85.1', sessionDefaults: { pollInterval: 50 }, threadTickMs: 50, log: line => logs.push(line), }); await launcher.listen(); return { ...f, runtime, launcher, logs, records, plan, cap: thread => caps.get(thread), close, business: boot.businesses.acme, setPlan: steps => writeFileSync(plan, JSON.stringify(steps) + '\n') }; } catch (e) { await close(); throw e; } }