UNPKG

@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
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)); }