@fusedashlabs/ui9000-workspace
Version:
UI9000 workspace MCP. One tool, show_workspace, draws a chart from your table. Set TYPESAFE_API_KEY to let Jev choose the chart.
127 lines (126 loc) • 4.84 kB
JavaScript
import { DATA_PROFILE_KEYS } from '../spec/data-profile.js';
import { SPEC_ACTION_SET } from '../spec/workspace-spec.js';
import { governTrace } from '../governance/govern-trace.js';
import { closedProfile } from '../trace/trace.js';
import { evalWhen } from './when.js';
const INTENT_MISMATCH = 'Component intents do not include this objective.';
const NO_ELIGIBILITY = 'No eligibility rule matched the profile.';
const BAD_WHEN = 'Catalog when-clause could not be evaluated.';
const MISSING_ID = 'Catalog entry is missing an id.';
export function decide(input) {
const { intent, catalog } = input;
const profile = closedProfile(input.profile);
const eligible = [];
const rejected = [];
const entries = Array.isArray(catalog) ? catalog : [];
for (const [index, entry] of entries.entries()) {
if (typeof entry?.id !== 'string' || !entry.id.trim()) {
rejected.push({ id: `missing-id:${index}`, reason: MISSING_ID });
continue;
}
const result = scoreEntry(entry, intent, profile);
if (result.kind === 'eligible') {
eligible.push({ id: entry.id, score: result.score, reasons: result.reasons });
}
else {
rejected.push({ id: entry.id, reason: result.reason });
}
}
eligible.sort((a, b) => b.score - a.score || a.id.localeCompare(b.id));
const winner = eligible[0] ?? null;
const runnerUp = eligible[1];
let tieBreak = 'no eligible candidate';
if (winner && runnerUp && winner.score === runnerUp.score) {
tieBreak = `tied at ${winner.score}; picked ${winner.id} before ${runnerUp.id} by id`;
}
else if (winner) {
tieBreak = `highest score ${winner.score} (${winner.id})`;
}
const winnerEntry = winner ? entries.find((entry) => entry.id === winner.id) : undefined;
const actions = closedActions(winnerEntry);
const trace = governTrace({
objective: intent,
profile,
candidates: eligible.map((item) => ({ ...item })),
rejections: rejected.map((item) => ({ ...item })),
actions,
tieBreak,
}, winnerEntry?.allowedActions ?? actions);
return { winner: winner?.id ?? null, eligible, rejected, trace };
}
function scoreEntry(entry, intent, profile) {
const intents = entry.intents ?? [];
if (intents.length === 0 || !intents.includes(intent)) {
return { kind: 'rejected', reason: INTENT_MISMATCH };
}
for (const rule of entry.disqualify ?? []) {
const matched = matchRule(rule, profile);
if (matched === 'bad-when')
return { kind: 'rejected', reason: BAD_WHEN };
if (matched)
return { kind: 'rejected', reason: sentence(rule.reason) };
}
const eligibility = entry.eligibility ?? [];
const matchedReasons = [];
for (const rule of eligibility) {
const matched = matchRule(rule, profile);
if (matched === 'bad-when')
return { kind: 'rejected', reason: BAD_WHEN };
if (matched)
matchedReasons.push(sentence(rule.reason));
}
if (matchedReasons.length === 0) {
return { kind: 'rejected', reason: NO_ELIGIBILITY };
}
let score = 10 + matchedReasons.length * 2;
for (const evalCase of entry.evalCases ?? []) {
if (!evalCaseApplies(evalCase, intent, profile))
continue;
score += 3;
if (evalCase.reasonIncludes) {
matchedReasons.push(`evalCase ${evalCase.id ?? 'eligible'}: ${evalCase.reasonIncludes}`);
}
}
return { kind: 'eligible', score, reasons: matchedReasons };
}
function evalCaseApplies(evalCase, intent, profile) {
if (evalCase.intent !== intent)
return false;
if (evalCase.expect !== 'eligible')
return false;
if (!evalCase.profile)
return false;
return profileWitnessMatches(profile, evalCase.profile);
}
/** Witness keys must be present and equal on the input profile. Empty witness never matches. */
function profileWitnessMatches(actual, rawWitness) {
const witness = closedProfile(rawWitness);
let sawKey = false;
for (const key of DATA_PROFILE_KEYS) {
if (witness[key] === undefined)
continue;
sawKey = true;
if (actual[key] !== witness[key])
return false;
}
return sawKey;
}
function matchRule(rule, profile) {
if (!rule.when?.trim())
return 'bad-when';
try {
return evalWhen(rule.when, profile);
}
catch {
return 'bad-when';
}
}
function sentence(reason) {
const text = reason?.trim() ?? '';
return text || 'Disqualified by a catalog rule.';
}
function closedActions(entry) {
if (!entry?.allowedActions)
return [];
return entry.allowedActions.filter((action) => SPEC_ACTION_SET.has(action));
}