mirror of
https://github.com/affaan-m/ECC.git
synced 2026-09-28 12:35:13 +02:00
134 lines
7.0 KiB
JavaScript
134 lines
7.0 KiB
JavaScript
'use strict';
|
|
|
|
const { loadContextRegistry, projectionFor, explainContextEntry } = require('./context-pack-registry');
|
|
const {
|
|
DEFAULT_REPO_ROOT, compilerDigest, createSourceReader, digestObject,
|
|
normalizeMetadataText, stableStringify, validateSchema, validateTarget,
|
|
} = require('./context-profile-support');
|
|
|
|
const PROFILE_ALIASES = Object.freeze({ lean: 'lean@1', full: 'full@1' });
|
|
const MODES = Object.freeze(['manual', 'suggest', 'auto']);
|
|
|
|
function loadContextProfile(profileId = 'lean@1', { repoRoot = DEFAULT_REPO_ROOT } = {}) {
|
|
const id = PROFILE_ALIASES[profileId] || profileId;
|
|
if (!['lean@1', 'full@1'].includes(id)) throw new Error(`Unknown context profile: ${profileId}`);
|
|
const source = createSourceReader(repoRoot).json(`manifests/context-profiles/${id}.json`);
|
|
validateSchema(source, 'context-profile.schema.json');
|
|
if (source.id !== id) throw new Error('Context profile source ID does not match the requested profile');
|
|
if ((id === 'lean@1' && (source.budget.mode !== 'blocking' || source.selection.eager === 'all'))
|
|
|| (id === 'full@1' && (source.budget.mode !== 'report-only' || source.selection.eager !== 'all'))) {
|
|
throw new Error('Profile selection and budget mode violate the versioned profile contract');
|
|
}
|
|
const canonical = {
|
|
...source,
|
|
description: normalizeMetadataText(source.description, 'Profile description'),
|
|
selection: {
|
|
...source.selection,
|
|
eager: source.selection.eager === 'all' ? 'all' : [...source.selection.eager].sort(),
|
|
required: [...source.selection.required].sort(),
|
|
},
|
|
};
|
|
return { ...canonical, profileDigest: digestObject(canonical) };
|
|
}
|
|
|
|
function validateSelectors(values, knownIds, label) {
|
|
if (!Array.isArray(values)) throw new Error(`${label} must be an array of skill IDs`);
|
|
const seen = new Set();
|
|
for (const id of values) {
|
|
if (typeof id !== 'string' || !knownIds.has(id)) throw new Error(`Unknown ${label} ID: ${id}`);
|
|
if (seen.has(id)) throw new Error(`Duplicate ${label} ID: ${id}`);
|
|
seen.add(id);
|
|
}
|
|
return [...seen].sort();
|
|
}
|
|
|
|
function resolveSelection(registry, profile, include, exclude) {
|
|
const byId = new Map(registry.entries.map(entry => [entry.id, entry]));
|
|
const known = new Set(byId.keys());
|
|
const additions = validateSelectors(include, known, 'include');
|
|
const removals = new Set(validateSelectors(exclude, known, 'exclude'));
|
|
const eager = profile.selection.eager === 'all' ? [...known] : validateSelectors(profile.selection.eager, known, 'profile');
|
|
const required = validateSelectors(profile.selection.required, known, 'required');
|
|
for (const id of required) {
|
|
if (!eager.includes(id)) throw new Error(`Profile is missing required eager ID: ${id}`);
|
|
if (removals.has(id)) throw new Error(`Cannot exclude required profile entry: ${id}`);
|
|
}
|
|
if (additions.some(id => removals.has(id))) throw new Error('Include and exclude selections overlap');
|
|
const selected = new Map();
|
|
function select(id, reason) {
|
|
if (removals.has(id)) throw new Error(`Required dependency closure excludes ${id}`);
|
|
if (selected.has(id)) return;
|
|
selected.set(id, reason);
|
|
byId.get(id).dependencies.forEach(dependency => select(dependency, `Required dependency of ${id}`));
|
|
}
|
|
eager.filter(id => !removals.has(id)).sort().forEach(id => select(id, 'Selected by context profile'));
|
|
additions.forEach(id => select(id, 'Explicitly included'));
|
|
return registry.entries.map(entry => ({
|
|
...entry,
|
|
selection: selected.has(entry.id) ? 'selected' : removals.has(entry.id) ? 'excluded' : 'routed',
|
|
reason: selected.get(entry.id) || (removals.has(entry.id) ? 'Explicitly excluded' : 'Available through routed discovery'),
|
|
}));
|
|
}
|
|
|
|
function estimateMetadata(entries, target, profile) {
|
|
const ledger = entries.filter(entry => entry.selection === 'selected').map(entry => {
|
|
const metadata = { harness: target, type: 'skill', name: entry.name, description: entry.description };
|
|
const renderedBytes = Buffer.byteLength(`${stableStringify(metadata)}\n`, 'utf8');
|
|
return { id: entry.id, renderedBytes, estimatedTokens: Math.ceil(renderedBytes / 4) };
|
|
});
|
|
const estimatedTokens = ledger.reduce((total, entry) => total + entry.estimatedTokens, 0);
|
|
return {
|
|
method: 'utf8-bytes-div-4@1', surface: 'skill-discovery-metadata',
|
|
renderedBytes: ledger.reduce((total, entry) => total + entry.renderedBytes, 0),
|
|
estimatedTokens, budgetTokens: profile.budget.tokens,
|
|
withinBudget: estimatedTokens <= profile.budget.tokens, budgetMode: profile.budget.mode,
|
|
nativeTokens: null, wrapperTokens: null, wholeScopeTokens: null, ledger,
|
|
};
|
|
}
|
|
|
|
function compileContextProfile({
|
|
repoRoot = DEFAULT_REPO_ROOT, profileId = 'lean@1', selectionMode = 'manual',
|
|
target = 'codex', include = [], exclude = [],
|
|
} = {}) {
|
|
validateTarget(target);
|
|
if (!MODES.includes(selectionMode)) throw new Error(`Unknown selection mode: ${selectionMode}`);
|
|
const registry = loadContextRegistry({ repoRoot });
|
|
const profile = loadContextProfile(profileId, { repoRoot });
|
|
if (profile.registryId !== registry.id) throw new Error('Profile registry ID mismatch');
|
|
const selected = resolveSelection(registry, profile, include, exclude);
|
|
const ids = selection => selected.filter(entry => entry.selection === selection).map(entry => entry.id);
|
|
const value = {
|
|
schemaVersion: 'ecc.context-plan.v1', profileId: profile.id, selectionMode, target,
|
|
disposition: 'proposed', active: false,
|
|
registryDigest: registry.registryDigest, profileDigest: profile.profileDigest,
|
|
compilerDigest: compilerDigest(),
|
|
selectedIds: ids('selected'), routedIds: ids('routed'), excludedIds: ids('excluded'),
|
|
entries: selected.map(entry => ({
|
|
id: entry.id, selection: entry.selection, reason: entry.reason,
|
|
sourcePath: entry.sourcePath, contentDigest: entry.contentDigest,
|
|
requiredResources: [...entry.requiredResources],
|
|
projection: projectionFor(entry, target),
|
|
})),
|
|
estimate: estimateMetadata(selected, target, profile),
|
|
excludedSurfaces: registry.excludedSurfaces,
|
|
limitations: [
|
|
'Read-only proposal; no harness activation, installation or permission change was attempted.',
|
|
'Selection modes are recorded intent; task routing and automatic switching are not implemented.',
|
|
'Only skill discovery metadata is estimated; provider counters, wrappers and whole-scope costs are unknown.',
|
|
'An estimate within 8000 tokens does not certify native context usage or successful discovery.',
|
|
'Dependency closure covers explicit declarations only; workflow dependency review is incomplete.',
|
|
'Install support is an owner-module declaration; it does not prove native exposure or execution.',
|
|
],
|
|
};
|
|
const plan = { ...value, planDigest: digestObject(value) };
|
|
if (!plan.estimate.withinBudget && plan.estimate.budgetMode === 'blocking') {
|
|
const error = new Error(`Context metadata estimate ${plan.estimate.estimatedTokens} exceeds the 8000-token ceiling`);
|
|
error.code = 'CONTEXT_PROFILE_BUDGET_EXCEEDED';
|
|
error.plan = plan;
|
|
throw error;
|
|
}
|
|
return plan;
|
|
}
|
|
|
|
module.exports = { compileContextProfile, explainContextEntry, loadContextProfile };
|