mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-09-16 05:02:21 +02:00
31 lines
1.4 KiB
JavaScript
31 lines
1.4 KiB
JavaScript
/** Availability is separate from execution and answer correctness. */
|
|
export function capabilityAvailable(contract, capability, expectedTools = []) {
|
|
const offered = (contract.offered || []).map(name => String(name).replace(/^mcp__email__/, ''));
|
|
return capability === null
|
|
|| (contract.active_capabilities || contract.capabilities || []).includes(capability)
|
|
|| (contract.routing_experiment === 'recent_model_choice'
|
|
&& expectedTools.some(tool => offered.includes(tool)));
|
|
}
|
|
|
|
/** Compare source steps in memory; callers retain booleans, never private text. */
|
|
export function skillDetailEvidence(output, answer) {
|
|
let text = String(output || '');
|
|
for (let i = 0; i < 3; i++) {
|
|
try {
|
|
const parsed = JSON.parse(text);
|
|
const inner = parsed.stdout ?? parsed.results ?? parsed.response;
|
|
if (typeof inner !== 'string') break;
|
|
text = inner;
|
|
} catch { break; }
|
|
}
|
|
const normalize = value => value.replace(/[`*_]/g, '').replace(/\s+/g, ' ').trim().toLowerCase();
|
|
const steps = [];
|
|
let selected = false;
|
|
for (const line of text.split('\n')) {
|
|
const heading = line.match(/^#{1,6}\s+(.+)/);
|
|
if (heading) { selected = /^(?:procedure|verification)$/i.test(heading[1].trim()); continue; }
|
|
if (selected && line.trim()) steps.push(normalize(line.replace(/^\s*(?:\d+[.)]|[-*])\s+/, '')));
|
|
}
|
|
return {steps: steps.length, covered: steps.length > 0 && steps.every(step => normalize(answer).includes(step))};
|
|
}
|