301 lines
12 KiB
JavaScript
301 lines
12 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.resolveAssistantCapabilityRuntimeBinding = resolveAssistantCapabilityRuntimeBinding;
|
|
exports.buildAssistantCapabilityRuntimeBindingFields = buildAssistantCapabilityRuntimeBindingFields;
|
|
exports.attachAssistantCapabilityRuntimeBinding = attachAssistantCapabilityRuntimeBinding;
|
|
const assistantRuntimeContracts_1 = require("../types/assistantRuntimeContracts");
|
|
const assistantRuntimeContractRegistry_1 = require("./assistantRuntimeContractRegistry");
|
|
const assistantRuntimeContractResolver_1 = require("./assistantRuntimeContractResolver");
|
|
const assistantStateTransitionRuntimeAdapter_1 = require("./assistantStateTransitionRuntimeAdapter");
|
|
const assistantTruthAnswerPolicyRuntimeAdapter_1 = require("./assistantTruthAnswerPolicyRuntimeAdapter");
|
|
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 uniqueStrings(values) {
|
|
return Array.from(new Set(values.filter((item) => item.trim().length > 0)));
|
|
}
|
|
function addViolation(target, violation) {
|
|
if (!target.includes(violation)) {
|
|
target.push(violation);
|
|
}
|
|
}
|
|
function resolveShadow(input, debug) {
|
|
return (input.runtimeContractShadow ??
|
|
toRecordObject(debug.assistant_runtime_contract_v1) ??
|
|
(0, assistantRuntimeContractResolver_1.resolveAssistantRuntimeContractShadow)({
|
|
addressDebug: debug,
|
|
addressRuntimeMeta: input.addressRuntimeMeta,
|
|
groundingStatus: input.groundingStatus
|
|
}));
|
|
}
|
|
function resolveTruthPolicy(input, debug) {
|
|
return (input.truthAnswerPolicy ??
|
|
toRecordObject(debug.assistant_truth_answer_policy_v1) ??
|
|
(0, assistantTruthAnswerPolicyRuntimeAdapter_1.resolveAssistantTruthAnswerPolicyRuntime)({
|
|
addressDebug: debug,
|
|
addressRuntimeMeta: input.addressRuntimeMeta,
|
|
groundingStatus: input.groundingStatus,
|
|
coverageReport: input.coverageReport,
|
|
replyType: input.replyType
|
|
}));
|
|
}
|
|
function resolveStateTransition(input, debug) {
|
|
return (input.stateTransition ??
|
|
toRecordObject(debug.assistant_state_transition_v1) ??
|
|
(0, assistantStateTransitionRuntimeAdapter_1.resolveAssistantStateTransitionRuntime)({
|
|
addressDebug: debug,
|
|
addressRuntimeMeta: input.addressRuntimeMeta,
|
|
groundingStatus: input.groundingStatus,
|
|
coverageReport: input.coverageReport,
|
|
replyType: input.replyType,
|
|
runtimeContractShadow: input.runtimeContractShadow,
|
|
truthAnswerPolicy: input.truthAnswerPolicy
|
|
}));
|
|
}
|
|
function hasValue(value) {
|
|
return toNonEmptyString(value) !== null;
|
|
}
|
|
function collectProvidedAnchors(debug) {
|
|
const anchors = [];
|
|
const filters = toRecordObject(debug.extracted_filters);
|
|
const semanticFrame = toRecordObject(debug.semantic_frame);
|
|
const rootFrame = toRecordObject(debug.address_root_frame_context);
|
|
const anchorType = toNonEmptyString(debug.anchor_type);
|
|
const anchorValue = toNonEmptyString(debug.anchor_value_resolved) ?? toNonEmptyString(debug.anchor_value_raw);
|
|
if (filters) {
|
|
for (const [key, value] of Object.entries(filters)) {
|
|
if (hasValue(value)) {
|
|
anchors.push(key);
|
|
}
|
|
}
|
|
}
|
|
if (anchorType && anchorType !== "unknown" && anchorValue) {
|
|
anchors.push(anchorType);
|
|
}
|
|
if (semanticFrame) {
|
|
const anchorKind = toNonEmptyString(semanticFrame.anchor_kind);
|
|
const anchorValueFromFrame = toNonEmptyString(semanticFrame.anchor_value);
|
|
if (anchorKind && anchorKind !== "none" && (anchorValueFromFrame || semanticFrame.selected_object_scope_detected === true)) {
|
|
anchors.push(anchorKind);
|
|
}
|
|
if (semanticFrame.selected_object_scope_detected === true) {
|
|
anchors.push("selected_object");
|
|
}
|
|
}
|
|
if (toNonEmptyString(rootFrame?.current_frame_kind)?.includes("drilldown")) {
|
|
anchors.push("selected_object");
|
|
}
|
|
return uniqueStrings(anchors);
|
|
}
|
|
function anchorSatisfied(requiredAnchor, providedAnchors, debug) {
|
|
const filters = toRecordObject(debug.extracted_filters);
|
|
if (providedAnchors.includes(requiredAnchor)) {
|
|
return true;
|
|
}
|
|
if (requiredAnchor === "item") {
|
|
return (providedAnchors.includes("selected_object") ||
|
|
providedAnchors.includes("anchor_item") ||
|
|
hasValue(filters?.item) ||
|
|
toNonEmptyString(debug.anchor_type) === "item");
|
|
}
|
|
if (requiredAnchor === "supplier") {
|
|
return providedAnchors.includes("counterparty") || hasValue(filters?.supplier) || hasValue(filters?.counterparty);
|
|
}
|
|
return false;
|
|
}
|
|
function runtimeLaneObserved(debug) {
|
|
const capabilityRouteMode = toNonEmptyString(debug.capability_route_mode);
|
|
const capabilityLayer = toNonEmptyString(debug.capability_layer);
|
|
const detectedMode = toNonEmptyString(debug.detected_mode);
|
|
if (capabilityRouteMode === "exact" && (capabilityLayer === "compute" || detectedMode === "address_query")) {
|
|
return "address_exact";
|
|
}
|
|
if (detectedMode === "assistant_data_scope") {
|
|
return "assistant_data_scope";
|
|
}
|
|
if (capabilityLayer === "conversational") {
|
|
return "chat";
|
|
}
|
|
return "unknown";
|
|
}
|
|
function focusObjectBindingStatus(input) {
|
|
if (!input.requiresFocusObject) {
|
|
return "not_required";
|
|
}
|
|
if (input.providedAnchors.includes("selected_object")) {
|
|
return "bound";
|
|
}
|
|
if (!input.missingAnchors.includes("item") && input.providedAnchors.includes("item")) {
|
|
return "inferred_from_anchor";
|
|
}
|
|
return "missing";
|
|
}
|
|
function truthFallbackAllowed(contractTruthFallbacks, truthMode) {
|
|
return truthMode === "confirmed" || contractTruthFallbacks.includes(truthMode);
|
|
}
|
|
function bindingStatusFor(input) {
|
|
if (!input.hasCapabilityId) {
|
|
return "not_applicable";
|
|
}
|
|
if (!input.hasContract) {
|
|
return "contract_missing";
|
|
}
|
|
if (input.violations.includes("transition_not_supported_by_capability") ||
|
|
input.violations.includes("required_anchor_missing") ||
|
|
input.violations.includes("focus_object_required_but_unbound") ||
|
|
input.violations.includes("runtime_lane_mismatch")) {
|
|
return "blocked";
|
|
}
|
|
if (input.violations.length > 0 || input.truthMode !== "confirmed") {
|
|
return "bound_with_limits";
|
|
}
|
|
return "bound";
|
|
}
|
|
function bindingActionFor(status, missingAnchors) {
|
|
if (status === "not_applicable" || status === "contract_missing") {
|
|
return "observe_only";
|
|
}
|
|
if (status === "blocked" && missingAnchors.length > 0) {
|
|
return "clarify";
|
|
}
|
|
if (status === "blocked") {
|
|
return "block";
|
|
}
|
|
if (status === "bound_with_limits") {
|
|
return "limit";
|
|
}
|
|
return "allow";
|
|
}
|
|
function resolveAssistantCapabilityRuntimeBinding(input) {
|
|
const debug = toRecordObject(input.addressDebug) ?? {};
|
|
const shadow = resolveShadow(input, debug);
|
|
const truthPolicy = resolveTruthPolicy(input, debug);
|
|
const stateTransition = resolveStateTransition(input, debug);
|
|
const capabilityId = shadow.capability_contract_id ??
|
|
toNonEmptyString(debug.capability_contract_id) ??
|
|
toNonEmptyString(debug.capability_id) ??
|
|
truthPolicy.answer_shape.capability_contract_id;
|
|
const capabilityContract = capabilityId ? (0, assistantRuntimeContractRegistry_1.getAssistantCapabilityContract)(capabilityId) : null;
|
|
const violations = [];
|
|
if (capabilityId && !capabilityContract) {
|
|
addViolation(violations, "capability_contract_missing");
|
|
}
|
|
const transitionAllowed = capabilityContract && stateTransition.transition_id
|
|
? capabilityContract.supported_transition_classes.includes(stateTransition.transition_id)
|
|
: capabilityContract
|
|
? null
|
|
: false;
|
|
if (capabilityContract && stateTransition.transition_id && !transitionAllowed) {
|
|
addViolation(violations, "transition_not_supported_by_capability");
|
|
}
|
|
const providedAnchors = collectProvidedAnchors(debug);
|
|
const requiredAnchors = capabilityContract?.required_anchors ?? [];
|
|
const missingAnchors = requiredAnchors.filter((anchor) => !anchorSatisfied(anchor, providedAnchors, debug));
|
|
if (missingAnchors.length > 0) {
|
|
addViolation(violations, "required_anchor_missing");
|
|
}
|
|
const focusStatus = focusObjectBindingStatus({
|
|
requiresFocusObject: capabilityContract?.requires_focus_object ?? false,
|
|
providedAnchors,
|
|
missingAnchors
|
|
});
|
|
if (capabilityContract?.requires_focus_object && focusStatus === "missing") {
|
|
addViolation(violations, "focus_object_required_but_unbound");
|
|
}
|
|
const observedLane = runtimeLaneObserved(debug);
|
|
const laneMatches = !capabilityContract || observedLane === "unknown" || capabilityContract.runtime_lane === observedLane;
|
|
if (capabilityContract && !laneMatches) {
|
|
addViolation(violations, "runtime_lane_mismatch");
|
|
}
|
|
const truthAllowed = capabilityContract
|
|
? truthFallbackAllowed(capabilityContract.truth_mode_fallbacks, truthPolicy.truth_gate.truth_mode)
|
|
: null;
|
|
if (truthAllowed === false) {
|
|
addViolation(violations, "truth_fallback_not_declared");
|
|
}
|
|
const answerShapeCompatible = capabilityContract && truthPolicy.answer_shape.capability_contract_id
|
|
? truthPolicy.answer_shape.capability_contract_id === capabilityContract.capability_id
|
|
: capabilityContract
|
|
? null
|
|
: false;
|
|
if (answerShapeCompatible === false && capabilityContract) {
|
|
addViolation(violations, "answer_shape_capability_mismatch");
|
|
}
|
|
const status = bindingStatusFor({
|
|
hasCapabilityId: Boolean(capabilityId),
|
|
hasContract: Boolean(capabilityContract),
|
|
violations,
|
|
truthMode: truthPolicy.truth_gate.truth_mode
|
|
});
|
|
const action = bindingActionFor(status, missingAnchors);
|
|
return {
|
|
schema_version: assistantRuntimeContracts_1.ASSISTANT_CAPABILITY_RUNTIME_BINDING_SCHEMA_VERSION,
|
|
binding_owner: "assistantCapabilityRuntimeBindingAdapter",
|
|
capability_id: capabilityId,
|
|
capability_contract_id: capabilityContract?.capability_id ?? null,
|
|
binding_status: status,
|
|
binding_action: action,
|
|
runtime_lane_expected: capabilityContract?.runtime_lane ?? null,
|
|
runtime_lane_observed: observedLane,
|
|
execution_adapter: capabilityContract?.execution_adapter ?? null,
|
|
transition_id: stateTransition.transition_id,
|
|
transition_allowed: transitionAllowed,
|
|
required_anchors: requiredAnchors,
|
|
provided_anchors: providedAnchors,
|
|
missing_anchors: missingAnchors,
|
|
requires_focus_object: capabilityContract?.requires_focus_object ?? false,
|
|
focus_object_binding_status: focusStatus,
|
|
result_shape: capabilityContract?.result_shape ?? null,
|
|
answer_object_shape: capabilityContract?.answer_object_shape ?? null,
|
|
truth_gate_behavior: capabilityContract?.coverage_gate_behavior ?? null,
|
|
truth_fallback_allowed: truthAllowed,
|
|
answer_shape_compatible: answerShapeCompatible,
|
|
violations,
|
|
reason_codes: uniqueStrings([
|
|
`binding_status_${status}`,
|
|
`binding_action_${action}`,
|
|
...violations,
|
|
...stateTransition.reason_codes,
|
|
...truthPolicy.truth_gate.reason_codes,
|
|
...shadow.capability_contract_reason
|
|
]).slice(0, 48)
|
|
};
|
|
}
|
|
function buildAssistantCapabilityRuntimeBindingFields(input) {
|
|
const binding = resolveAssistantCapabilityRuntimeBinding(input);
|
|
return {
|
|
assistant_capability_binding_v1: binding,
|
|
capability_binding_contract: binding,
|
|
capability_binding_status: binding.binding_status,
|
|
capability_binding_action: binding.binding_action,
|
|
capability_binding_violations: binding.violations
|
|
};
|
|
}
|
|
function attachAssistantCapabilityRuntimeBinding(debugPayload, input) {
|
|
return {
|
|
...debugPayload,
|
|
...buildAssistantCapabilityRuntimeBindingFields({
|
|
...input,
|
|
addressDebug: debugPayload
|
|
})
|
|
};
|
|
}
|