NODEDC_1C/llm_normalizer/backend/src/services/assistantMcpDiscoveryRuntim...

131 lines
4.7 KiB
TypeScript

import {
buildAssistantMcpDiscoveryAnswerDraft,
type AssistantMcpDiscoveryAnswerDraftContract
} from "./assistantMcpDiscoveryAnswerAdapter";
import {
executeAssistantMcpDiscoveryPilot,
type AssistantMcpDiscoveryPilotExecutionContract,
type AssistantMcpDiscoveryPilotExecutorDeps
} from "./assistantMcpDiscoveryPilotExecutor";
import {
planAssistantMcpDiscovery,
type AssistantMcpDiscoveryMetadataSurfaceRef,
type AssistantMcpDiscoveryPlannerContract
} from "./assistantMcpDiscoveryPlanner";
import type { AssistantMcpDiscoveryDataNeedGraphContract } from "./assistantMcpDiscoveryDataNeedGraph";
import type { AssistantMcpDiscoveryTurnMeaningRef } from "./assistantMcpDiscoveryPolicy";
export const ASSISTANT_MCP_DISCOVERY_RUNTIME_BRIDGE_SCHEMA_VERSION =
"assistant_mcp_discovery_runtime_bridge_v1" as const;
export type AssistantMcpDiscoveryRuntimeBridgeStatus =
| "answer_draft_ready"
| "checked_sources_only"
| "needs_clarification"
| "blocked"
| "unsupported";
export interface AssistantMcpDiscoveryRuntimeBridgeInput {
semanticDataNeed?: string | null;
dataNeedGraph?: AssistantMcpDiscoveryDataNeedGraphContract | null;
metadataSurface?: AssistantMcpDiscoveryMetadataSurfaceRef | null;
turnMeaning?: AssistantMcpDiscoveryTurnMeaningRef | null;
deps?: AssistantMcpDiscoveryPilotExecutorDeps;
}
export interface AssistantMcpDiscoveryRuntimeBridgeContract {
schema_version: typeof ASSISTANT_MCP_DISCOVERY_RUNTIME_BRIDGE_SCHEMA_VERSION;
policy_owner: "assistantMcpDiscoveryRuntimeBridge";
bridge_status: AssistantMcpDiscoveryRuntimeBridgeStatus;
hot_runtime_wired: false;
planner: AssistantMcpDiscoveryPlannerContract;
pilot: AssistantMcpDiscoveryPilotExecutionContract;
answer_draft: AssistantMcpDiscoveryAnswerDraftContract;
user_facing_response_allowed: boolean;
business_fact_answer_allowed: boolean;
requires_user_clarification: boolean;
reason_codes: string[];
}
function normalizeReasonCode(value: string): string | null {
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: string[], value: string): void {
const normalized = normalizeReasonCode(value);
if (normalized && !target.includes(normalized)) {
target.push(normalized);
}
}
function uniqueStrings(values: string[]): string[] {
const result: string[] = [];
for (const value of values) {
const text = String(value ?? "").trim();
if (text && !result.includes(text)) {
result.push(text);
}
}
return result;
}
function bridgeStatusFor(
pilot: AssistantMcpDiscoveryPilotExecutionContract,
draft: AssistantMcpDiscoveryAnswerDraftContract
): AssistantMcpDiscoveryRuntimeBridgeStatus {
if (draft.answer_mode === "blocked" || pilot.pilot_status === "blocked") {
return "blocked";
}
if (draft.answer_mode === "needs_clarification" || pilot.pilot_status === "skipped_needs_clarification") {
return "needs_clarification";
}
if (pilot.pilot_status === "unsupported") {
return "unsupported";
}
if (draft.answer_mode === "checked_sources_only") {
return "checked_sources_only";
}
return "answer_draft_ready";
}
function businessFactAnswerAllowed(draft: AssistantMcpDiscoveryAnswerDraftContract): boolean {
return draft.answer_mode === "confirmed_with_bounded_inference" || draft.answer_mode === "bounded_inference_only";
}
export async function runAssistantMcpDiscoveryRuntimeBridge(
input: AssistantMcpDiscoveryRuntimeBridgeInput
): Promise<AssistantMcpDiscoveryRuntimeBridgeContract> {
const planner = planAssistantMcpDiscovery({
semanticDataNeed: input.semanticDataNeed,
dataNeedGraph: input.dataNeedGraph,
metadataSurface: input.metadataSurface,
turnMeaning: input.turnMeaning
});
const pilot = await executeAssistantMcpDiscoveryPilot(planner, input.deps);
const answerDraft = buildAssistantMcpDiscoveryAnswerDraft(pilot);
const bridgeStatus = bridgeStatusFor(pilot, answerDraft);
const reasonCodes = uniqueStrings([...planner.reason_codes, ...pilot.reason_codes, ...answerDraft.reason_codes]);
pushReason(reasonCodes, `runtime_bridge_status_${bridgeStatus}`);
pushReason(reasonCodes, "runtime_bridge_not_wired_to_hot_assistant_answer");
return {
schema_version: ASSISTANT_MCP_DISCOVERY_RUNTIME_BRIDGE_SCHEMA_VERSION,
policy_owner: "assistantMcpDiscoveryRuntimeBridge",
bridge_status: bridgeStatus,
hot_runtime_wired: false,
planner,
pilot,
answer_draft: answerDraft,
user_facing_response_allowed: bridgeStatus !== "blocked",
business_fact_answer_allowed: businessFactAnswerAllowed(answerDraft),
requires_user_clarification: bridgeStatus === "needs_clarification",
reason_codes: reasonCodes
};
}