@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env node
|
||||
// No-model native resource-loader check: real project discovery plus seat extensions.
|
||||
// Usage: node scripts/test-goal-discovery.mjs <cwd> <settings.json> conflict|explicit|shared|unified [brain]
|
||||
// Reads only the extensions list; never copies credentials, starts sessions or runs tools.
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdtempSync, readFileSync, realpathSync, rmSync, existsSync, unlinkSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { dirname, join, relative } from 'node:path';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
|
||||
const [cwd, settingsPath, expected, brain = cwd] = process.argv.slice(2);
|
||||
assert.ok(cwd && settingsPath && ['conflict', 'explicit', 'shared', 'unified'].includes(expected));
|
||||
const wantsShared = expected === 'shared' || expected === 'unified';
|
||||
const extensions = JSON.parse(readFileSync(settingsPath, 'utf8')).extensions;
|
||||
assert.ok(Array.isArray(extensions) && extensions.every(x => typeof x === 'string'));
|
||||
const agentDir = mkdtempSync(join(tmpdir(), 'goal-discovery-'));
|
||||
process.env.PI_CODING_AGENT_DIR = agentDir;
|
||||
process.env.MOSAIC_LAUNCH_INCARNATION = 'discovery-test-' + randomUUID();
|
||||
process.env.PI_OFFLINE = '1';
|
||||
// Loading the shared extension must not quarantine any operator-owned legacy state.
|
||||
for (const dir of new Set([cwd, brain])) {
|
||||
assert.equal(existsSync(join(dir, '.pi/state/goal/goal-state.json')), false);
|
||||
}
|
||||
try {
|
||||
const binary = realpathSync(execFileSync('which', ['pi'], { encoding: 'utf8' }).trim());
|
||||
const { DefaultResourceLoader, SettingsManager } = await import(pathToFileURL(join(dirname(binary), 'index.js')).href);
|
||||
const settingsManager = SettingsManager.inMemory({ extensions });
|
||||
settingsManager.setProjectTrusted(true);
|
||||
const loader = new DefaultResourceLoader({ cwd, agentDir, settingsManager,
|
||||
noExtensions: expected === 'explicit' || expected === 'shared',
|
||||
additionalExtensionPaths: [...(expected === 'shared'
|
||||
? [join(brain, '.pi/extensions/goal'), join(brain, 'fleet/extensions/wrapper-guard')]
|
||||
: expected === 'explicit' ? extensions : []), join(brain, 'tools/unslop-hook/extension.ts')],
|
||||
noSkills: true, noPromptTemplates: true, noThemes: true, noContextFiles: true });
|
||||
await loader.reload();
|
||||
const loaded = loader.getExtensions();
|
||||
if (expected === 'conflict') {
|
||||
assert.ok(loaded.errors.some(e => e.error.includes('Tool "goal_report" conflicts with')), JSON.stringify(loaded.errors));
|
||||
console.log('PASS negative control: combined discovery reproduces goal_report conflict');
|
||||
} else {
|
||||
assert.deepEqual(loaded.errors, []);
|
||||
const owners = loaded.extensions.filter(e => e.tools.has('goal_report'));
|
||||
assert.equal(owners.length, 1);
|
||||
assert.equal(realpathSync(owners[0].path.replace(/\/index\.ts$/, '')),
|
||||
realpathSync(join(brain, wantsShared ? '.pi/extensions/goal' : 'fleet/extensions/goal')));
|
||||
const guardNames = extensions.filter(p => /\/(wrapper-guard|mosaic-core)$/.test(p)).map(p => p.split('/').at(-1));
|
||||
assert.ok(guardNames.length, 'test must include the configured enforcement extension');
|
||||
for (const name of guardNames) {
|
||||
const guards = loaded.extensions.filter(e => e.path.replace(/\/index\.ts$/, '').endsWith('/' + name));
|
||||
assert.equal(guards.length, 1, `${name} must remain loaded`);
|
||||
assert.ok(guards[0].handlers.get('tool_call')?.length, `${name} interception must remain registered`);
|
||||
}
|
||||
assert.equal(owners[0].commands.has('goal'), true);
|
||||
assert.equal(loaded.extensions.filter(e => e.path.endsWith('/unslop-hook/extension.ts')).length, 1,
|
||||
'launcher unslop extension must remain loaded');
|
||||
if (wantsShared) {
|
||||
assert.ok(owners[0].shortcuts.has('alt+g'), 'NG full recall shortcut must be registered');
|
||||
}
|
||||
// Only the synthetic native suite opts into an owned fixture write. Live checks
|
||||
// merely load registrations and never run a goal command or read private state.
|
||||
if (process.env.MOSAIC_GOAL_DISCOVERY_FIXTURE_ROOT === brain) {
|
||||
assert.ok(brain.startsWith(join(tmpdir(), 'goal58-native-')));
|
||||
assert.equal(readFileSync(join(brain, '.goal-discovery-owned-fixture'), 'utf8'), 'synthetic goal58 fixture');
|
||||
const filename = `goal-state.${process.env.MOSAIC_LAUNCH_INCARNATION}.json`;
|
||||
const possible = [...new Set([join(brain, '.pi/state/goal', filename),
|
||||
join(brain, 'fleet/state/goal', filename), join(cwd, '.pi/state/goal', filename)])];
|
||||
assert.ok(possible.every(p => !existsSync(p)), 'fixture identity must be new');
|
||||
await owners[0].commands.get('goal').handler('owned goal58 fixture', { mode: 'print', hasUI: false });
|
||||
const written = possible.filter(existsSync);
|
||||
assert.equal(written.length, 1, 'one incarnation state store per selected entrypoint');
|
||||
assert.equal(JSON.parse(readFileSync(written[0], 'utf8')).text, 'owned goal58 fixture');
|
||||
console.log(`PASS owned fixture state path: ${relative(brain, dirname(written[0]))}`);
|
||||
unlinkSync(written[0]);
|
||||
}
|
||||
console.log(`PASS ${expected} discovery: exactly one ${wantsShared ? 'shared NG' : 'legacy'} goal_report and /goal; ${guardNames.join(', ')} interception and unslop retained`);
|
||||
}
|
||||
} finally {
|
||||
rmSync(agentDir, { recursive: true, force: true });
|
||||
}
|
||||
Reference in New Issue
Block a user