254 lines
9.6 KiB
JavaScript
254 lines
9.6 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.ADDRESS_TRUTH_GATE_SCHEMA_VERSION = void 0;
|
|
exports.resolveAddressTruthGate = resolveAddressTruthGate;
|
|
exports.toAddressTruthGateContract = toAddressTruthGateContract;
|
|
exports.buildAddressTruthGateDebugFields = buildAddressTruthGateDebugFields;
|
|
exports.attachAddressTruthGate = attachAddressTruthGate;
|
|
exports.ADDRESS_TRUTH_GATE_SCHEMA_VERSION = "address_truth_gate_v1";
|
|
function toRecordObject(value) {
|
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
return null;
|
|
}
|
|
return value;
|
|
}
|
|
function toNonEmptyString(value) {
|
|
if (value === null || value === undefined) {
|
|
return null;
|
|
}
|
|
const text = String(value).trim();
|
|
return text.length > 0 ? text : null;
|
|
}
|
|
function toStringList(value) {
|
|
if (!Array.isArray(value)) {
|
|
return [];
|
|
}
|
|
return value
|
|
.map((item) => toNonEmptyString(item))
|
|
.filter((item) => Boolean(item));
|
|
}
|
|
function isTruthGateStatus(value) {
|
|
return (value === "full_confirmed" ||
|
|
value === "partial_supported" ||
|
|
value === "blocked_missing_anchor" ||
|
|
value === "blocked_route_expectation_failure" ||
|
|
value === "blocked_execution_error" ||
|
|
value === "limited_temporal_or_contextual" ||
|
|
value === "unknown");
|
|
}
|
|
function isCarryoverDepth(value) {
|
|
return value === "full" || value === "root_only" || value === "object_only" || value === "meta_only" || value === "none";
|
|
}
|
|
function isLimitedReasonCategory(value) {
|
|
return (value === "empty_match" ||
|
|
value === "missing_anchor" ||
|
|
value === "recipe_visibility_gap" ||
|
|
value === "execution_error" ||
|
|
value === "unsupported");
|
|
}
|
|
function isRuntimeReadiness(value) {
|
|
return (value === "LIVE_QUERYABLE" ||
|
|
value === "LIVE_QUERYABLE_WITH_LIMITS" ||
|
|
value === "REQUIRES_SPECIALIZED_RECIPE" ||
|
|
value === "DEEP_ONLY" ||
|
|
value === "UNKNOWN");
|
|
}
|
|
function normalizeReasonCode(value) {
|
|
const normalized = value
|
|
.trim()
|
|
.replace(/[^\p{L}\p{N}_.:-]+/gu, "_")
|
|
.replace(/^_+|_+$/g, "")
|
|
.toLowerCase();
|
|
return normalized.length > 0 ? normalized.slice(0, 120) : null;
|
|
}
|
|
function pushReason(target, value) {
|
|
const text = toNonEmptyString(value);
|
|
if (!text) {
|
|
return;
|
|
}
|
|
const normalized = normalizeReasonCode(text);
|
|
if (normalized && !target.includes(normalized)) {
|
|
target.push(normalized);
|
|
}
|
|
}
|
|
function hasFilterScope(filters) {
|
|
if (!filters) {
|
|
return false;
|
|
}
|
|
const scopeKeys = [
|
|
"period_from",
|
|
"period_to",
|
|
"as_of_date",
|
|
"organization",
|
|
"counterparty",
|
|
"contract",
|
|
"account",
|
|
"item",
|
|
"warehouse",
|
|
"document_type",
|
|
"document_ref",
|
|
"status"
|
|
];
|
|
return scopeKeys.some((key) => toNonEmptyString(filters[key]) !== null);
|
|
}
|
|
function hasObjectFocus(input) {
|
|
if (toNonEmptyString(input.filters?.item)) {
|
|
return true;
|
|
}
|
|
if (input.semanticFrame?.selected_object_scope_detected) {
|
|
return true;
|
|
}
|
|
if (input.semanticFrame?.anchor_kind === "selected_object" || input.semanticFrame?.anchor_kind === "item") {
|
|
return true;
|
|
}
|
|
return (input.intent === "inventory_purchase_provenance_for_item" ||
|
|
input.intent === "inventory_purchase_documents_for_item" ||
|
|
input.intent === "inventory_sale_trace_for_item" ||
|
|
input.intent === "inventory_profitability_for_item" ||
|
|
input.intent === "inventory_purchase_to_sale_chain" ||
|
|
input.intent === "inventory_aging_by_purchase_date");
|
|
}
|
|
function hasReusableRootScope(input) {
|
|
if (hasFilterScope(input.filters)) {
|
|
return true;
|
|
}
|
|
return Boolean(input.semanticFrame && input.semanticFrame.scope_kind !== "none");
|
|
}
|
|
function truthGateStatusFrom(input) {
|
|
if (input.truthGateStatusHint) {
|
|
return input.truthGateStatusHint;
|
|
}
|
|
const missingRequiredFilters = input.missingRequiredFilters ?? [];
|
|
const reasonCodes = input.reasons ?? [];
|
|
const heuristicOpenItemsFallback = Boolean(input.intent === "open_items_by_counterparty_or_contract" &&
|
|
(reasonCodes.includes("confirmed_balance_unavailable_fallback_to_heuristic_candidates") ||
|
|
reasonCodes.includes("open_items_account_query_override_to_movements")));
|
|
if (input.routeExpectationStatus === "mismatch") {
|
|
return "blocked_route_expectation_failure";
|
|
}
|
|
if (input.limitedReasonCategory === "execution_error") {
|
|
return "blocked_execution_error";
|
|
}
|
|
if (missingRequiredFilters.length > 0 || input.limitedReasonCategory === "missing_anchor") {
|
|
return "blocked_missing_anchor";
|
|
}
|
|
if (input.temporalGuardOutcome === "ambiguous_limited" || input.temporalAlignmentStatus === "conflicting") {
|
|
return "limited_temporal_or_contextual";
|
|
}
|
|
if (input.replyType === "factual" && input.limitedReasonCategory === "empty_match") {
|
|
return "full_confirmed";
|
|
}
|
|
if (heuristicOpenItemsFallback) {
|
|
return "partial_supported";
|
|
}
|
|
if (input.limitedReasonCategory === "empty_match" ||
|
|
input.limitedReasonCategory === "recipe_visibility_gap" ||
|
|
input.limitedReasonCategory === "unsupported" ||
|
|
input.replyType === "partial_coverage") {
|
|
return "partial_supported";
|
|
}
|
|
if ((input.rowsMatched ?? 0) > 0) {
|
|
return "full_confirmed";
|
|
}
|
|
return "unknown";
|
|
}
|
|
function carryoverEligibilityFor(input, truthGateStatus) {
|
|
if (truthGateStatus.startsWith("blocked")) {
|
|
return "none";
|
|
}
|
|
if (input.limitedReasonCategory === "recipe_visibility_gap" || input.limitedReasonCategory === "unsupported") {
|
|
return "meta_only";
|
|
}
|
|
if (hasObjectFocus(input)) {
|
|
return "object_only";
|
|
}
|
|
if (truthGateStatus === "limited_temporal_or_contextual") {
|
|
return hasReusableRootScope(input) ? "root_only" : "meta_only";
|
|
}
|
|
if (truthGateStatus === "full_confirmed" || truthGateStatus === "partial_supported") {
|
|
return hasReusableRootScope(input) ? "root_only" : "meta_only";
|
|
}
|
|
return "none";
|
|
}
|
|
function explanationFor(truthGateStatus, limitedReasonCategory) {
|
|
if (truthGateStatus === "full_confirmed") {
|
|
return null;
|
|
}
|
|
if (truthGateStatus === "blocked_missing_anchor") {
|
|
return "required_anchor_missing";
|
|
}
|
|
if (truthGateStatus === "blocked_route_expectation_failure") {
|
|
return "route_expectation_failed";
|
|
}
|
|
if (truthGateStatus === "blocked_execution_error") {
|
|
return "execution_failed";
|
|
}
|
|
if (truthGateStatus === "limited_temporal_or_contextual") {
|
|
return "temporal_or_contextual_limit";
|
|
}
|
|
if (limitedReasonCategory === "recipe_visibility_gap") {
|
|
return "specialized_recipe_required";
|
|
}
|
|
if (limitedReasonCategory === "unsupported") {
|
|
return "unsupported_in_current_contour";
|
|
}
|
|
return truthGateStatus === "partial_supported" ? "evidence_or_coverage_is_partial" : "truth_gate_not_confirmed";
|
|
}
|
|
function collectReasonCodes(input, truthGateStatus) {
|
|
const reasons = [];
|
|
pushReason(reasons, `address_truth_gate_${truthGateStatus}`);
|
|
pushReason(reasons, input.routeExpectationReason);
|
|
pushReason(reasons, input.limitedReasonCategory ? `limited_category_${input.limitedReasonCategory}` : null);
|
|
(input.missingRequiredFilters ?? []).forEach((item) => pushReason(reasons, `missing_filter_${item}`));
|
|
(input.limitations ?? []).forEach((item) => pushReason(reasons, item));
|
|
(input.reasons ?? []).forEach((item) => pushReason(reasons, item));
|
|
return reasons.slice(0, 32);
|
|
}
|
|
function resolveAddressTruthGate(input) {
|
|
const truthGateStatus = truthGateStatusFrom(input);
|
|
return {
|
|
schema_version: exports.ADDRESS_TRUTH_GATE_SCHEMA_VERSION,
|
|
policy_owner: "addressTruthGatePolicy",
|
|
truth_gate_status: truthGateStatus,
|
|
carryover_eligibility: carryoverEligibilityFor(input, truthGateStatus),
|
|
limited_reason_category: input.limitedReasonCategory ?? null,
|
|
runtime_readiness: input.runtimeReadiness ?? "UNKNOWN",
|
|
reason_codes: collectReasonCodes(input, truthGateStatus),
|
|
blocked_or_limited_explanation: explanationFor(truthGateStatus, input.limitedReasonCategory ?? null)
|
|
};
|
|
}
|
|
function toAddressTruthGateContract(value) {
|
|
const record = toRecordObject(value);
|
|
if (!record) {
|
|
return null;
|
|
}
|
|
const truthGateStatus = toNonEmptyString(record.truth_gate_status);
|
|
const carryoverEligibility = toNonEmptyString(record.carryover_eligibility);
|
|
const limitedReasonCategory = toNonEmptyString(record.limited_reason_category);
|
|
const runtimeReadiness = toNonEmptyString(record.runtime_readiness);
|
|
if (!isTruthGateStatus(truthGateStatus) || !isCarryoverDepth(carryoverEligibility) || !isRuntimeReadiness(runtimeReadiness)) {
|
|
return null;
|
|
}
|
|
return {
|
|
schema_version: exports.ADDRESS_TRUTH_GATE_SCHEMA_VERSION,
|
|
policy_owner: "addressTruthGatePolicy",
|
|
truth_gate_status: truthGateStatus,
|
|
carryover_eligibility: carryoverEligibility,
|
|
limited_reason_category: isLimitedReasonCategory(limitedReasonCategory) ? limitedReasonCategory : null,
|
|
runtime_readiness: runtimeReadiness,
|
|
reason_codes: toStringList(record.reason_codes).slice(0, 32),
|
|
blocked_or_limited_explanation: toNonEmptyString(record.blocked_or_limited_explanation)
|
|
};
|
|
}
|
|
function buildAddressTruthGateDebugFields(input) {
|
|
return {
|
|
address_truth_gate_v1: resolveAddressTruthGate(input)
|
|
};
|
|
}
|
|
function attachAddressTruthGate(debugPayload, input) {
|
|
return {
|
|
...debugPayload,
|
|
...buildAddressTruthGateDebugFields(input)
|
|
};
|
|
}
|