Lumi/plugins/lumi_ai/backend/scope_manager.js
2026-06-12 11:54:46 +02:00

63 lines
2.9 KiB
JavaScript

const HARD_RULES = Object.freeze([
"Do not perform destructive actions without a validated backend workflow and explicit confirmation.",
"Do not bypass permissions or impersonate users.",
"Do not directly edit source files, databases, configuration files, or system files.",
"Do not execute shell commands from model output.",
"Do not generate or execute raw SQL.",
"Do not forge transaction, moderation, or configuration history.",
"Do not assist abuse, privilege escalation, data theft, or permission bypass.",
"Do not claim an action was performed unless the backend workflow confirms it."
]);
const DEFAULT_SCOPE = Object.freeze({
allowed_topics: "Lumi Bot, its WebUI, installed plugins, community features, stream tools, moderation tools, and bot configuration.",
allowed_support_domains: "Lumi operation, configuration, troubleshooting, navigation, and installed plugin support.",
answer_style: "Helpful, concise, factual, and purpose-built for Lumi support.",
linking_behavior: "Use verified internal WebUI links when available.",
repo_lookup_enabled: true,
allow_deterministic_help_shortcuts: false,
allow_moderator_code_help: false,
clarification_behavior: "Ask a concise clarification question when the user's intended product, page, or setting is ambiguous.",
max_answer_length: 4000,
role_overrides: { admin: "", mod: "", user: "" }
});
function normalizeScope(value = {}) {
const defined = Object.fromEntries(
Object.entries(value || {}).filter(([, entry]) => entry !== undefined && entry !== null)
);
return {
...DEFAULT_SCOPE,
...defined,
repo_lookup_enabled: defined.repo_lookup_enabled !== false,
allow_deterministic_help_shortcuts: defined.allow_deterministic_help_shortcuts === true,
allow_moderator_code_help: defined.allow_moderator_code_help === true,
max_answer_length: bounded(defined.max_answer_length, 100, 4000, DEFAULT_SCOPE.max_answer_length),
role_overrides: { ...DEFAULT_SCOPE.role_overrides, ...(defined.role_overrides || {}) }
};
}
function buildPolicy({ scope, role }) {
const normalized = normalizeScope(scope);
return {
hard_rules: [...HARD_RULES],
normal_scope: [
`Allowed topics: ${normalized.allowed_topics}`,
`Allowed support domains: ${normalized.allowed_support_domains}`,
`Linking behavior: ${normalized.linking_behavior}`,
`Clarification behavior: ${normalized.clarification_behavior}`,
normalized.role_overrides[role] ? `Role-specific scope: ${normalized.role_overrides[role]}` : ""
].filter(Boolean),
style: normalized.answer_style,
max_answer_length: normalized.max_answer_length,
repo_lookup_enabled: normalized.repo_lookup_enabled
};
}
function bounded(value, min, max, fallback) {
const number = Number.parseInt(value, 10);
return Number.isFinite(number) ? Math.min(max, Math.max(min, number)) : fallback;
}
module.exports = { HARD_RULES, DEFAULT_SCOPE, normalizeScope, buildPolicy };