ARCH: добавить runtime bridge MCP discovery
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
import {
|
||||
buildAssistantMcpDiscoveryAnswerDraft,
|
||||
type AssistantMcpDiscoveryAnswerDraftContract
|
||||
} from "./assistantMcpDiscoveryAnswerAdapter";
|
||||
import {
|
||||
executeAssistantMcpDiscoveryPilot,
|
||||
type AssistantMcpDiscoveryPilotExecutionContract,
|
||||
type AssistantMcpDiscoveryPilotExecutorDeps
|
||||
} from "./assistantMcpDiscoveryPilotExecutor";
|
||||
import {
|
||||
planAssistantMcpDiscovery,
|
||||
type AssistantMcpDiscoveryPlannerContract
|
||||
} from "./assistantMcpDiscoveryPlanner";
|
||||
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;
|
||||
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,
|
||||
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
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user