"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.loadCapabilitiesRegistry = loadCapabilitiesRegistry; exports.buildCapabilityContractReplyFromRegistry = buildCapabilityContractReplyFromRegistry; exports.resolveNearestCapabilityGroup = resolveNearestCapabilityGroup; const fs_1 = __importDefault(require("fs")); const config_1 = require("../config"); const FALLBACK_REGISTRY = { schema_version: "capabilities_registry_fallback_v1", updated_at: "2026-04-09T00:00:00.000Z", assistant_mode: "read_only", groups: [ { group_code: "vat", group_title: "НДС", description: "Срезы и расчёты НДС на базе данных 1С", risk_level: "high", maturity_status: "partial", supported_operations: ["vat_period_snapshot", "vat_payable_forecast"], unsupported_operations: ["submit_tax_declaration"], required_entities: ["period", "organization"], optional_entities: ["counterparty"], typical_queries: ["Сколько НДС к уплате за период?", "Покажи срез НДС на дату"], related_routes: [], safe_alternatives: ["Показать движения по 68/19 за период"], one_c_hints: ["РегистрБухгалтерии.Хозрасчетный"] }, { group_code: "counterparties", group_title: "Контрагенты", description: "Документы, операции, договоры и активность по контрагентам", risk_level: "medium", maturity_status: "production_ready", supported_operations: ["list_documents_by_counterparty", "list_contracts_by_counterparty"], unsupported_operations: ["edit_counterparty_card"], required_entities: ["counterparty_scope_or_contract"], optional_entities: ["period", "organization"], typical_queries: ["Покажи документы по контрагенту", "Какие операции были по банку с контрагентом?"], related_routes: [], safe_alternatives: ["Уточнить ИНН или наименование контрагента"], one_c_hints: ["Справочник.Контрагенты"] }, { group_code: "settlements", group_title: "Долги и расчёты", description: "Сальдо, хвосты, незакрытые авансы и аналитика по расчётам", risk_level: "high", maturity_status: "production_ready", supported_operations: ["receivables_confirmed_as_of_date", "open_items_by_counterparty_or_contract"], unsupported_operations: ["close_period"], required_entities: ["period_or_date"], optional_entities: ["organization", "account", "counterparty"], typical_queries: ["Кто нам должен на дату?", "Хвосты покажи по счёту 60 за период"], related_routes: [], safe_alternatives: ["Уточнить период, счёт или организацию"], one_c_hints: ["РегистрБухгалтерии.Хозрасчетный"] }, { group_code: "cash", group_title: "Деньги", description: "Остатки и движение по денежным счетам и кассе", risk_level: "medium", maturity_status: "production_ready", supported_operations: ["account_balance_snapshot", "bank_operations_by_counterparty"], unsupported_operations: ["post_bank_statement"], required_entities: ["date_or_period"], optional_entities: ["organization", "account", "counterparty"], typical_queries: ["Какой остаток по счёту 51 на дату?", "Покажи движение денег за месяц"], related_routes: [], safe_alternatives: ["Уточнить счёт или период"], one_c_hints: ["РегистрБухгалтерии.Хозрасчетный"] }, { group_code: "inventory", group_title: "Склад и товары", description: "Подтверждённые остатки, происхождение и документы по товарным позициям", risk_level: "medium", maturity_status: "production_ready", supported_operations: [ "inventory_on_hand_as_of_date", "inventory_purchase_provenance_for_item", "inventory_purchase_documents_for_item" ], unsupported_operations: ["write_off_inventory"], required_entities: ["date_or_period"], optional_entities: ["organization", "warehouse", "item"], typical_queries: ["Какие товары сейчас лежат на складе?", "Кто поставил эту позицию?"], related_routes: [], safe_alternatives: ["Уточнить организацию, дату или позицию"], one_c_hints: ["РегистрБухгалтерии.Хозрасчетный"] }, { group_code: "boundaries", group_title: "Ограничения", description: "Операции, которые ассистент не выполняет в этом рантайме", risk_level: "high", maturity_status: "production_ready", supported_operations: ["explain_boundary", "suggest_safe_next_step"], unsupported_operations: ["configure_1c", "admin_server_actions", "create_or_post_documents"], required_entities: [], optional_entities: [], typical_queries: ["Можешь настроить 1С?"], related_routes: [], safe_alternatives: ["Сформировать безопасный план диагностики для 1С или ИТ-админа"], one_c_hints: [] } ] }; let cache = null; function toRecord(value) { if (!value || typeof value !== "object" || Array.isArray(value)) return null; return value; } function toStringSafe(value) { if (typeof value !== "string") return null; const trimmed = value.trim(); return trimmed.length > 0 ? trimmed : null; } function toArray(value) { return Array.isArray(value) ? value : []; } function readRegistryFromFile() { if (!fs_1.default.existsSync(config_1.ASSISTANT_CAPABILITIES_REGISTRY_FILE)) return null; try { const raw = fs_1.default.readFileSync(config_1.ASSISTANT_CAPABILITIES_REGISTRY_FILE, "utf-8"); const parsed = JSON.parse(raw); const root = toRecord(parsed); if (!root) return null; const groups = toArray(root.groups) .map((item) => toRecord(item)) .filter((item) => item !== null) .map((item) => ({ group_code: toStringSafe(item.group_code) ?? "unknown_group", group_title: toStringSafe(item.group_title) ?? "Группа", description: toStringSafe(item.description) ?? "", risk_level: toStringSafe(item.risk_level) ?? "medium", maturity_status: toStringSafe(item.maturity_status) ?? "partial", supported_operations: toArray(item.supported_operations) .map((v) => toStringSafe(v)) .filter((v) => v !== null), unsupported_operations: toArray(item.unsupported_operations) .map((v) => toStringSafe(v)) .filter((v) => v !== null), required_entities: toArray(item.required_entities) .map((v) => toStringSafe(v)) .filter((v) => v !== null), optional_entities: toArray(item.optional_entities) .map((v) => toStringSafe(v)) .filter((v) => v !== null), typical_queries: toArray(item.typical_queries) .map((v) => toStringSafe(v)) .filter((v) => v !== null), related_routes: toArray(item.related_routes) .map((v) => toStringSafe(v)) .filter((v) => v !== null), safe_alternatives: toArray(item.safe_alternatives) .map((v) => toStringSafe(v)) .filter((v) => v !== null), one_c_hints: toArray(item.one_c_hints) .map((v) => toStringSafe(v)) .filter((v) => v !== null) })); if (groups.length === 0) return null; return { schema_version: toStringSafe(root.schema_version) ?? "capabilities_registry_v1", updated_at: toStringSafe(root.updated_at) ?? new Date().toISOString(), assistant_mode: toStringSafe(root.assistant_mode) ?? "read_only", groups }; } catch { return null; } } function loadCapabilitiesRegistry() { try { const mtimeMs = fs_1.default.existsSync(config_1.ASSISTANT_CAPABILITIES_REGISTRY_FILE) ? fs_1.default.statSync(config_1.ASSISTANT_CAPABILITIES_REGISTRY_FILE).mtimeMs : -1; if (cache && cache.mtimeMs === mtimeMs) { return cache.value; } const value = readRegistryFromFile() ?? FALLBACK_REGISTRY; cache = { mtimeMs, value }; return value; } catch { return cache?.value ?? FALLBACK_REGISTRY; } } function buildCapabilityContractReplyFromRegistry() { const registry = loadCapabilitiesRegistry(); const topGroups = registry.groups.filter((group) => group.group_code !== "boundaries").slice(0, 6); const groupLines = topGroups.map((group, index) => { const examples = group.typical_queries .slice(0, 2) .map((query) => query.trim()) .filter((query) => query.length > 0) .join("; "); return `${index + 1}. ${group.group_title}: ${group.description}${examples ? `. Например: ${examples}` : "."}`; }); return [ "Могу помочь с вопросами по данным 1С в режиме чтения: НДС, контрагенты, долги, деньги и склад.", "По основным группам:", ...groupLines, "Если нужно, подскажу, как лучше сформулировать запрос под вашу задачу.", "Что не делаю: не настраиваю 1С, не меняю конфигурацию, не создаю и не провожу документы, не выполняю админ-действия на сервере." ].join("\n"); } function resolveNearestCapabilityGroup(input) { const registry = loadCapabilitiesRegistry(); const haystack = `${String(input.domain ?? "")} ${String(input.queryClass ?? "")}`.toLowerCase(); if (!haystack.trim()) return null; const scoring = registry.groups.map((group) => { let score = 0; const bucket = `${group.group_code} ${group.group_title} ${group.description} ${group.supported_operations.join(" ")}`.toLowerCase(); for (const token of haystack.split(/[\s._/-]+/g).filter(Boolean)) { if (bucket.includes(token)) score += 1; } return { group, score }; }); scoring.sort((a, b) => b.score - a.score); return scoring[0] && scoring[0].score > 0 ? scoring[0].group : null; }