Добавить handoff допуска для автономного value-flow
This commit is contained in:
parent
d64d7f84cc
commit
5125c747d8
|
|
@ -42,6 +42,11 @@ function isEvidencePlannerContract(value) {
|
|||
const record = toRecordObject(value);
|
||||
return record?.schema_version === "assistant_evidence_planner_v1" && record?.policy_owner === "assistantEvidencePlanner";
|
||||
}
|
||||
function isExecutionHandoffContract(value) {
|
||||
const record = toRecordObject(value);
|
||||
return (record?.schema_version === "assistant_mcp_discovery_execution_handoff_v1" &&
|
||||
record?.policy_owner === "assistantMcpDiscoveryExecutionHandoff");
|
||||
}
|
||||
function resolveEntryPoint(input) {
|
||||
if (isMcpDiscoveryEntryPointContract(input.entryPoint)) {
|
||||
return input.entryPoint;
|
||||
|
|
@ -61,6 +66,7 @@ function buildAssistantMcpDiscoveryDebugAttachmentFields(input) {
|
|||
const evidencePlanAnswerContract = toRecordObject(evidencePlan?.answer_contract);
|
||||
const chainAlignment = toRecordObject(planner?.catalog_chain_template_alignment);
|
||||
const routeCandidate = isRouteCandidateContract(bridge?.route_candidate) ? bridge.route_candidate : null;
|
||||
const executionHandoff = isExecutionHandoffContract(bridge?.execution_handoff) ? bridge.execution_handoff : null;
|
||||
const answerDraft = toRecordObject(bridge?.answer_draft);
|
||||
return {
|
||||
assistant_mcp_discovery_entry_point_v1: entryPoint,
|
||||
|
|
@ -91,6 +97,10 @@ function buildAssistantMcpDiscoveryDebugAttachmentFields(input) {
|
|||
mcp_discovery_route_candidate_executable_now: routeCandidate?.executable_now === true,
|
||||
mcp_discovery_route_candidate_enablement_reason: toNonEmptyString(routeCandidate?.enablement_reason),
|
||||
mcp_discovery_route_candidate_next_action: toNonEmptyString(routeCandidate?.recommended_next_action),
|
||||
mcp_discovery_execution_handoff_v1: executionHandoff,
|
||||
mcp_discovery_execution_handoff_status: toNonEmptyString(executionHandoff?.handoff_status),
|
||||
mcp_discovery_execution_handoff_allowed_hot_chain: executionHandoff?.allowed_hot_chain === true,
|
||||
mcp_discovery_execution_handoff_can_use_guarded_response: executionHandoff?.can_use_guarded_response === true,
|
||||
mcp_discovery_answer_mode: toNonEmptyString(answerDraft?.answer_mode),
|
||||
mcp_discovery_business_fact_answer_allowed: bridge?.business_fact_answer_allowed === true,
|
||||
mcp_discovery_user_facing_response_allowed: bridge?.user_facing_response_allowed === true,
|
||||
|
|
|
|||
83
llm_normalizer/backend/dist/services/assistantMcpDiscoveryExecutionHandoff.js
vendored
Normal file
83
llm_normalizer/backend/dist/services/assistantMcpDiscoveryExecutionHandoff.js
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ASSISTANT_MCP_DISCOVERY_EXECUTION_HANDOFF_SCHEMA_VERSION = void 0;
|
||||
exports.buildAssistantMcpDiscoveryExecutionHandoff = buildAssistantMcpDiscoveryExecutionHandoff;
|
||||
exports.ASSISTANT_MCP_DISCOVERY_EXECUTION_HANDOFF_SCHEMA_VERSION = "assistant_mcp_discovery_execution_handoff_v1";
|
||||
const HOT_HANDOFF_CHAIN_ALLOWLIST = ["value_flow"];
|
||||
function uniqueStrings(values) {
|
||||
const result = [];
|
||||
for (const value of values) {
|
||||
const text = String(value ?? "").trim();
|
||||
if (text && !result.includes(text)) {
|
||||
result.push(text);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function handoffStatusFor(input, allowedHotChain) {
|
||||
if (input.bridgeStatus === "needs_clarification" || input.routeCandidate.candidate_status === "needs_user_scope") {
|
||||
return "awaiting_user_scope";
|
||||
}
|
||||
if (input.bridgeStatus === "checked_sources_only") {
|
||||
return "checked_sources_only";
|
||||
}
|
||||
if (input.bridgeStatus === "blocked" ||
|
||||
input.bridgeStatus === "unsupported" ||
|
||||
input.routeCandidate.candidate_status === "blocked" ||
|
||||
input.routeCandidate.candidate_status === "needs_route_enablement") {
|
||||
return "blocked";
|
||||
}
|
||||
if (!allowedHotChain) {
|
||||
return "not_enabled_for_chain";
|
||||
}
|
||||
return "ready_for_guarded_response";
|
||||
}
|
||||
function buildAssistantMcpDiscoveryExecutionHandoff(input) {
|
||||
const allowedHotChain = HOT_HANDOFF_CHAIN_ALLOWLIST.includes(input.routeCandidate.selected_chain_id);
|
||||
const baseStatus = handoffStatusFor(input, allowedHotChain);
|
||||
const readinessChecksPassed = baseStatus === "ready_for_guarded_response" &&
|
||||
input.bridgeStatus === "answer_draft_ready" &&
|
||||
input.routeCandidate.candidate_status === "ready_for_reviewed_execution" &&
|
||||
input.routeCandidate.executable_now === true &&
|
||||
input.pilot.pilot_status === "executed" &&
|
||||
input.pilot.mcp_execution_performed === true &&
|
||||
input.businessFactAnswerAllowed === true &&
|
||||
input.userFacingResponseAllowed === true &&
|
||||
input.answerDraft.internal_mechanics_allowed === false;
|
||||
const handoffStatus = readinessChecksPassed ? "ready_for_guarded_response" : baseStatus;
|
||||
const reasonCodes = uniqueStrings([
|
||||
`execution_handoff_status_${handoffStatus}`,
|
||||
allowedHotChain ? "execution_handoff_chain_allowlisted" : "execution_handoff_chain_not_allowlisted",
|
||||
input.routeCandidate.executable_now
|
||||
? "execution_handoff_route_candidate_executable"
|
||||
: "execution_handoff_route_candidate_not_executable",
|
||||
input.pilot.mcp_execution_performed
|
||||
? "execution_handoff_mcp_execution_performed"
|
||||
: "execution_handoff_mcp_execution_not_performed",
|
||||
input.businessFactAnswerAllowed
|
||||
? "execution_handoff_business_fact_allowed"
|
||||
: "execution_handoff_business_fact_not_allowed",
|
||||
input.userFacingResponseAllowed
|
||||
? "execution_handoff_user_facing_allowed"
|
||||
: "execution_handoff_user_facing_not_allowed",
|
||||
readinessChecksPassed
|
||||
? "execution_handoff_guarded_response_ready"
|
||||
: "execution_handoff_guarded_response_not_ready"
|
||||
]);
|
||||
return {
|
||||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_EXECUTION_HANDOFF_SCHEMA_VERSION,
|
||||
policy_owner: "assistantMcpDiscoveryExecutionHandoff",
|
||||
handoff_status: handoffStatus,
|
||||
selected_chain_id: input.routeCandidate.selected_chain_id,
|
||||
route_candidate_status: input.routeCandidate.candidate_status,
|
||||
evidence_answer_mode: input.routeCandidate.evidence_answer_mode,
|
||||
evidence_expected_coverage: input.routeCandidate.evidence_expected_coverage,
|
||||
pilot_status: input.pilot.pilot_status,
|
||||
answer_mode: input.answerDraft.answer_mode,
|
||||
mcp_execution_performed: input.pilot.mcp_execution_performed,
|
||||
allowed_hot_chain: allowedHotChain,
|
||||
can_use_guarded_response: readinessChecksPassed,
|
||||
must_keep_internal_mechanics_hidden: true,
|
||||
reason_codes: reasonCodes
|
||||
};
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ exports.ASSISTANT_MCP_ROUTE_CANDIDATE_SCHEMA_VERSION = exports.ASSISTANT_MCP_DIS
|
|||
exports.runAssistantMcpDiscoveryRuntimeBridge = runAssistantMcpDiscoveryRuntimeBridge;
|
||||
const assistantMcpDiscoveryAnswerAdapter_1 = require("./assistantMcpDiscoveryAnswerAdapter");
|
||||
const assistantMcpDiscoveryPilotExecutor_1 = require("./assistantMcpDiscoveryPilotExecutor");
|
||||
const assistantMcpDiscoveryExecutionHandoff_1 = require("./assistantMcpDiscoveryExecutionHandoff");
|
||||
const assistantMcpDiscoveryPlanner_1 = require("./assistantMcpDiscoveryPlanner");
|
||||
exports.ASSISTANT_MCP_DISCOVERY_RUNTIME_BRIDGE_SCHEMA_VERSION = "assistant_mcp_discovery_runtime_bridge_v1";
|
||||
exports.ASSISTANT_MCP_DISCOVERY_LOOP_STATE_SCHEMA_VERSION = "assistant_mcp_discovery_loop_state_v1";
|
||||
|
|
@ -258,12 +259,26 @@ async function runAssistantMcpDiscoveryRuntimeBridge(input) {
|
|||
const bridgeStatus = bridgeStatusFor(pilot, answerDraft);
|
||||
const loopState = buildLoopState(planner, pilot, bridgeStatus);
|
||||
const routeCandidate = buildRouteCandidate(planner, pilot, bridgeStatus);
|
||||
const userFacingResponseAllowed = bridgeStatus !== "blocked";
|
||||
const businessFactAllowed = businessFactAnswerAllowed(answerDraft);
|
||||
const executionHandoff = (0, assistantMcpDiscoveryExecutionHandoff_1.buildAssistantMcpDiscoveryExecutionHandoff)({
|
||||
bridgeStatus,
|
||||
routeCandidate,
|
||||
pilot,
|
||||
answerDraft,
|
||||
businessFactAnswerAllowed: businessFactAllowed,
|
||||
userFacingResponseAllowed
|
||||
});
|
||||
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");
|
||||
pushReason(reasonCodes, `runtime_bridge_loop_state_${loopState.loop_status}`);
|
||||
pushReason(reasonCodes, "runtime_bridge_route_candidate_built");
|
||||
pushReason(reasonCodes, `runtime_bridge_route_candidate_${routeCandidate.candidate_status}`);
|
||||
pushReason(reasonCodes, `runtime_bridge_execution_handoff_${executionHandoff.handoff_status}`);
|
||||
for (const reasonCode of executionHandoff.reason_codes) {
|
||||
pushReason(reasonCodes, reasonCode);
|
||||
}
|
||||
return {
|
||||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_RUNTIME_BRIDGE_SCHEMA_VERSION,
|
||||
policy_owner: "assistantMcpDiscoveryRuntimeBridge",
|
||||
|
|
@ -274,8 +289,9 @@ async function runAssistantMcpDiscoveryRuntimeBridge(input) {
|
|||
answer_draft: answerDraft,
|
||||
loop_state: loopState,
|
||||
route_candidate: routeCandidate,
|
||||
user_facing_response_allowed: bridgeStatus !== "blocked",
|
||||
business_fact_answer_allowed: businessFactAnswerAllowed(answerDraft),
|
||||
execution_handoff: executionHandoff,
|
||||
user_facing_response_allowed: userFacingResponseAllowed,
|
||||
business_fact_answer_allowed: businessFactAllowed,
|
||||
requires_user_clarification: bridgeStatus === "needs_clarification",
|
||||
reason_codes: reasonCodes
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import type { AssistantMcpDiscoveryRuntimeEntryPointContract } from "./assistantMcpDiscoveryRuntimeEntryPoint";
|
||||
import type { AssistantMcpRouteCandidateContract } from "./assistantMcpDiscoveryRuntimeBridge";
|
||||
import type { AssistantMcpDiscoveryExecutionHandoffContract } from "./assistantMcpDiscoveryExecutionHandoff";
|
||||
import type { AssistantEvidencePlannerContract } from "./assistantEvidencePlanner";
|
||||
|
||||
export interface AssistantMcpDiscoveryDebugAttachmentFields {
|
||||
|
|
@ -31,6 +32,10 @@ export interface AssistantMcpDiscoveryDebugAttachmentFields {
|
|||
mcp_discovery_route_candidate_executable_now: boolean;
|
||||
mcp_discovery_route_candidate_enablement_reason: string | null;
|
||||
mcp_discovery_route_candidate_next_action: string | null;
|
||||
mcp_discovery_execution_handoff_v1: AssistantMcpDiscoveryExecutionHandoffContract | null;
|
||||
mcp_discovery_execution_handoff_status: string | null;
|
||||
mcp_discovery_execution_handoff_allowed_hot_chain: boolean;
|
||||
mcp_discovery_execution_handoff_can_use_guarded_response: boolean;
|
||||
mcp_discovery_answer_mode: string | null;
|
||||
mcp_discovery_business_fact_answer_allowed: boolean;
|
||||
mcp_discovery_user_facing_response_allowed: boolean;
|
||||
|
|
@ -92,6 +97,14 @@ function isEvidencePlannerContract(value: unknown): value is AssistantEvidencePl
|
|||
return record?.schema_version === "assistant_evidence_planner_v1" && record?.policy_owner === "assistantEvidencePlanner";
|
||||
}
|
||||
|
||||
function isExecutionHandoffContract(value: unknown): value is AssistantMcpDiscoveryExecutionHandoffContract {
|
||||
const record = toRecordObject(value);
|
||||
return (
|
||||
record?.schema_version === "assistant_mcp_discovery_execution_handoff_v1" &&
|
||||
record?.policy_owner === "assistantMcpDiscoveryExecutionHandoff"
|
||||
);
|
||||
}
|
||||
|
||||
function resolveEntryPoint(input: AttachAssistantMcpDiscoveryDebugInput): AssistantMcpDiscoveryRuntimeEntryPointContract | null {
|
||||
if (isMcpDiscoveryEntryPointContract(input.entryPoint)) {
|
||||
return input.entryPoint;
|
||||
|
|
@ -115,6 +128,7 @@ export function buildAssistantMcpDiscoveryDebugAttachmentFields(
|
|||
const evidencePlanAnswerContract = toRecordObject(evidencePlan?.answer_contract);
|
||||
const chainAlignment = toRecordObject(planner?.catalog_chain_template_alignment);
|
||||
const routeCandidate = isRouteCandidateContract(bridge?.route_candidate) ? bridge.route_candidate : null;
|
||||
const executionHandoff = isExecutionHandoffContract(bridge?.execution_handoff) ? bridge.execution_handoff : null;
|
||||
const answerDraft = toRecordObject(bridge?.answer_draft);
|
||||
|
||||
return {
|
||||
|
|
@ -146,6 +160,10 @@ export function buildAssistantMcpDiscoveryDebugAttachmentFields(
|
|||
mcp_discovery_route_candidate_executable_now: routeCandidate?.executable_now === true,
|
||||
mcp_discovery_route_candidate_enablement_reason: toNonEmptyString(routeCandidate?.enablement_reason),
|
||||
mcp_discovery_route_candidate_next_action: toNonEmptyString(routeCandidate?.recommended_next_action),
|
||||
mcp_discovery_execution_handoff_v1: executionHandoff,
|
||||
mcp_discovery_execution_handoff_status: toNonEmptyString(executionHandoff?.handoff_status),
|
||||
mcp_discovery_execution_handoff_allowed_hot_chain: executionHandoff?.allowed_hot_chain === true,
|
||||
mcp_discovery_execution_handoff_can_use_guarded_response: executionHandoff?.can_use_guarded_response === true,
|
||||
mcp_discovery_answer_mode: toNonEmptyString(answerDraft?.answer_mode),
|
||||
mcp_discovery_business_fact_answer_allowed: bridge?.business_fact_answer_allowed === true,
|
||||
mcp_discovery_user_facing_response_allowed: bridge?.user_facing_response_allowed === true,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,134 @@
|
|||
import type { AssistantMcpDiscoveryAnswerDraftContract } from "./assistantMcpDiscoveryAnswerAdapter";
|
||||
import type { AssistantMcpDiscoveryPilotExecutionContract } from "./assistantMcpDiscoveryPilotExecutor";
|
||||
import type { AssistantMcpDiscoveryChainId } from "./assistantMcpDiscoveryPlanner";
|
||||
import type {
|
||||
AssistantMcpDiscoveryRuntimeBridgeStatus,
|
||||
AssistantMcpRouteCandidateContract
|
||||
} from "./assistantMcpDiscoveryRuntimeBridge";
|
||||
|
||||
export const ASSISTANT_MCP_DISCOVERY_EXECUTION_HANDOFF_SCHEMA_VERSION =
|
||||
"assistant_mcp_discovery_execution_handoff_v1" as const;
|
||||
|
||||
export type AssistantMcpDiscoveryExecutionHandoffStatus =
|
||||
| "ready_for_guarded_response"
|
||||
| "awaiting_user_scope"
|
||||
| "checked_sources_only"
|
||||
| "not_enabled_for_chain"
|
||||
| "blocked";
|
||||
|
||||
export interface AssistantMcpDiscoveryExecutionHandoffInput {
|
||||
bridgeStatus: AssistantMcpDiscoveryRuntimeBridgeStatus;
|
||||
routeCandidate: AssistantMcpRouteCandidateContract;
|
||||
pilot: AssistantMcpDiscoveryPilotExecutionContract;
|
||||
answerDraft: AssistantMcpDiscoveryAnswerDraftContract;
|
||||
businessFactAnswerAllowed: boolean;
|
||||
userFacingResponseAllowed: boolean;
|
||||
}
|
||||
|
||||
export interface AssistantMcpDiscoveryExecutionHandoffContract {
|
||||
schema_version: typeof ASSISTANT_MCP_DISCOVERY_EXECUTION_HANDOFF_SCHEMA_VERSION;
|
||||
policy_owner: "assistantMcpDiscoveryExecutionHandoff";
|
||||
handoff_status: AssistantMcpDiscoveryExecutionHandoffStatus;
|
||||
selected_chain_id: AssistantMcpDiscoveryChainId;
|
||||
route_candidate_status: AssistantMcpRouteCandidateContract["candidate_status"];
|
||||
evidence_answer_mode: string | null;
|
||||
evidence_expected_coverage: string | null;
|
||||
pilot_status: AssistantMcpDiscoveryPilotExecutionContract["pilot_status"];
|
||||
answer_mode: AssistantMcpDiscoveryAnswerDraftContract["answer_mode"];
|
||||
mcp_execution_performed: boolean;
|
||||
allowed_hot_chain: boolean;
|
||||
can_use_guarded_response: boolean;
|
||||
must_keep_internal_mechanics_hidden: true;
|
||||
reason_codes: string[];
|
||||
}
|
||||
|
||||
const HOT_HANDOFF_CHAIN_ALLOWLIST: AssistantMcpDiscoveryChainId[] = ["value_flow"];
|
||||
|
||||
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 handoffStatusFor(
|
||||
input: AssistantMcpDiscoveryExecutionHandoffInput,
|
||||
allowedHotChain: boolean
|
||||
): AssistantMcpDiscoveryExecutionHandoffStatus {
|
||||
if (input.bridgeStatus === "needs_clarification" || input.routeCandidate.candidate_status === "needs_user_scope") {
|
||||
return "awaiting_user_scope";
|
||||
}
|
||||
if (input.bridgeStatus === "checked_sources_only") {
|
||||
return "checked_sources_only";
|
||||
}
|
||||
if (
|
||||
input.bridgeStatus === "blocked" ||
|
||||
input.bridgeStatus === "unsupported" ||
|
||||
input.routeCandidate.candidate_status === "blocked" ||
|
||||
input.routeCandidate.candidate_status === "needs_route_enablement"
|
||||
) {
|
||||
return "blocked";
|
||||
}
|
||||
if (!allowedHotChain) {
|
||||
return "not_enabled_for_chain";
|
||||
}
|
||||
return "ready_for_guarded_response";
|
||||
}
|
||||
|
||||
export function buildAssistantMcpDiscoveryExecutionHandoff(
|
||||
input: AssistantMcpDiscoveryExecutionHandoffInput
|
||||
): AssistantMcpDiscoveryExecutionHandoffContract {
|
||||
const allowedHotChain = HOT_HANDOFF_CHAIN_ALLOWLIST.includes(input.routeCandidate.selected_chain_id);
|
||||
const baseStatus = handoffStatusFor(input, allowedHotChain);
|
||||
const readinessChecksPassed =
|
||||
baseStatus === "ready_for_guarded_response" &&
|
||||
input.bridgeStatus === "answer_draft_ready" &&
|
||||
input.routeCandidate.candidate_status === "ready_for_reviewed_execution" &&
|
||||
input.routeCandidate.executable_now === true &&
|
||||
input.pilot.pilot_status === "executed" &&
|
||||
input.pilot.mcp_execution_performed === true &&
|
||||
input.businessFactAnswerAllowed === true &&
|
||||
input.userFacingResponseAllowed === true &&
|
||||
input.answerDraft.internal_mechanics_allowed === false;
|
||||
const handoffStatus = readinessChecksPassed ? "ready_for_guarded_response" : baseStatus;
|
||||
const reasonCodes = uniqueStrings([
|
||||
`execution_handoff_status_${handoffStatus}`,
|
||||
allowedHotChain ? "execution_handoff_chain_allowlisted" : "execution_handoff_chain_not_allowlisted",
|
||||
input.routeCandidate.executable_now
|
||||
? "execution_handoff_route_candidate_executable"
|
||||
: "execution_handoff_route_candidate_not_executable",
|
||||
input.pilot.mcp_execution_performed
|
||||
? "execution_handoff_mcp_execution_performed"
|
||||
: "execution_handoff_mcp_execution_not_performed",
|
||||
input.businessFactAnswerAllowed
|
||||
? "execution_handoff_business_fact_allowed"
|
||||
: "execution_handoff_business_fact_not_allowed",
|
||||
input.userFacingResponseAllowed
|
||||
? "execution_handoff_user_facing_allowed"
|
||||
: "execution_handoff_user_facing_not_allowed",
|
||||
readinessChecksPassed
|
||||
? "execution_handoff_guarded_response_ready"
|
||||
: "execution_handoff_guarded_response_not_ready"
|
||||
]);
|
||||
|
||||
return {
|
||||
schema_version: ASSISTANT_MCP_DISCOVERY_EXECUTION_HANDOFF_SCHEMA_VERSION,
|
||||
policy_owner: "assistantMcpDiscoveryExecutionHandoff",
|
||||
handoff_status: handoffStatus,
|
||||
selected_chain_id: input.routeCandidate.selected_chain_id,
|
||||
route_candidate_status: input.routeCandidate.candidate_status,
|
||||
evidence_answer_mode: input.routeCandidate.evidence_answer_mode,
|
||||
evidence_expected_coverage: input.routeCandidate.evidence_expected_coverage,
|
||||
pilot_status: input.pilot.pilot_status,
|
||||
answer_mode: input.answerDraft.answer_mode,
|
||||
mcp_execution_performed: input.pilot.mcp_execution_performed,
|
||||
allowed_hot_chain: allowedHotChain,
|
||||
can_use_guarded_response: readinessChecksPassed,
|
||||
must_keep_internal_mechanics_hidden: true,
|
||||
reason_codes: reasonCodes
|
||||
};
|
||||
}
|
||||
|
|
@ -7,6 +7,10 @@ import {
|
|||
type AssistantMcpDiscoveryPilotExecutionContract,
|
||||
type AssistantMcpDiscoveryPilotExecutorDeps
|
||||
} from "./assistantMcpDiscoveryPilotExecutor";
|
||||
import {
|
||||
buildAssistantMcpDiscoveryExecutionHandoff,
|
||||
type AssistantMcpDiscoveryExecutionHandoffContract
|
||||
} from "./assistantMcpDiscoveryExecutionHandoff";
|
||||
import {
|
||||
planAssistantMcpDiscovery,
|
||||
type AssistantMcpDiscoveryChainId,
|
||||
|
|
@ -101,6 +105,7 @@ export interface AssistantMcpDiscoveryRuntimeBridgeContract {
|
|||
answer_draft: AssistantMcpDiscoveryAnswerDraftContract;
|
||||
loop_state: AssistantMcpDiscoveryLoopStateContract;
|
||||
route_candidate: AssistantMcpRouteCandidateContract;
|
||||
execution_handoff: AssistantMcpDiscoveryExecutionHandoffContract;
|
||||
user_facing_response_allowed: boolean;
|
||||
business_fact_answer_allowed: boolean;
|
||||
requires_user_clarification: boolean;
|
||||
|
|
@ -414,6 +419,16 @@ export async function runAssistantMcpDiscoveryRuntimeBridge(
|
|||
const bridgeStatus = bridgeStatusFor(pilot, answerDraft);
|
||||
const loopState = buildLoopState(planner, pilot, bridgeStatus);
|
||||
const routeCandidate = buildRouteCandidate(planner, pilot, bridgeStatus);
|
||||
const userFacingResponseAllowed = bridgeStatus !== "blocked";
|
||||
const businessFactAllowed = businessFactAnswerAllowed(answerDraft);
|
||||
const executionHandoff = buildAssistantMcpDiscoveryExecutionHandoff({
|
||||
bridgeStatus,
|
||||
routeCandidate,
|
||||
pilot,
|
||||
answerDraft,
|
||||
businessFactAnswerAllowed: businessFactAllowed,
|
||||
userFacingResponseAllowed
|
||||
});
|
||||
const reasonCodes = uniqueStrings([...planner.reason_codes, ...pilot.reason_codes, ...answerDraft.reason_codes]);
|
||||
|
||||
pushReason(reasonCodes, `runtime_bridge_status_${bridgeStatus}`);
|
||||
|
|
@ -421,6 +436,10 @@ export async function runAssistantMcpDiscoveryRuntimeBridge(
|
|||
pushReason(reasonCodes, `runtime_bridge_loop_state_${loopState.loop_status}`);
|
||||
pushReason(reasonCodes, "runtime_bridge_route_candidate_built");
|
||||
pushReason(reasonCodes, `runtime_bridge_route_candidate_${routeCandidate.candidate_status}`);
|
||||
pushReason(reasonCodes, `runtime_bridge_execution_handoff_${executionHandoff.handoff_status}`);
|
||||
for (const reasonCode of executionHandoff.reason_codes) {
|
||||
pushReason(reasonCodes, reasonCode);
|
||||
}
|
||||
|
||||
return {
|
||||
schema_version: ASSISTANT_MCP_DISCOVERY_RUNTIME_BRIDGE_SCHEMA_VERSION,
|
||||
|
|
@ -432,8 +451,9 @@ export async function runAssistantMcpDiscoveryRuntimeBridge(
|
|||
answer_draft: answerDraft,
|
||||
loop_state: loopState,
|
||||
route_candidate: routeCandidate,
|
||||
user_facing_response_allowed: bridgeStatus !== "blocked",
|
||||
business_fact_answer_allowed: businessFactAnswerAllowed(answerDraft),
|
||||
execution_handoff: executionHandoff,
|
||||
user_facing_response_allowed: userFacingResponseAllowed,
|
||||
business_fact_answer_allowed: businessFactAllowed,
|
||||
requires_user_clarification: bridgeStatus === "needs_clarification",
|
||||
reason_codes: reasonCodes
|
||||
};
|
||||
|
|
|
|||
|
|
@ -15,12 +15,12 @@ function entryPointContract(overrides: Record<string, unknown> = {}) {
|
|||
business_fact_answer_allowed: true,
|
||||
requires_user_clarification: false,
|
||||
planner: {
|
||||
selected_chain_id: "value_flow_ranking",
|
||||
selected_chain_id: "value_flow",
|
||||
evidence_plan: {
|
||||
schema_version: "assistant_evidence_planner_v1",
|
||||
policy_owner: "assistantEvidencePlanner",
|
||||
planner_status: "ready_for_execution",
|
||||
selected_chain_id: "value_flow_ranking",
|
||||
selected_chain_id: "value_flow",
|
||||
evidence_axes: {
|
||||
missing_axes: []
|
||||
},
|
||||
|
|
@ -31,10 +31,10 @@ function entryPointContract(overrides: Record<string, unknown> = {}) {
|
|||
answer_mode: "confirmed_business_answer"
|
||||
}
|
||||
},
|
||||
catalog_chain_template_matches: ["value_flow_ranking", "value_flow"],
|
||||
catalog_chain_template_matches: ["value_flow"],
|
||||
catalog_chain_template_alignment: {
|
||||
alignment_status: "selected_matches_top",
|
||||
top_chain_template_match: "value_flow_ranking",
|
||||
top_chain_template_match: "value_flow",
|
||||
selected_chain_template_rank: 1,
|
||||
selected_chain_is_catalog_template: true,
|
||||
selected_chain_in_catalog_matches: true,
|
||||
|
|
@ -45,9 +45,9 @@ function entryPointContract(overrides: Record<string, unknown> = {}) {
|
|||
schema_version: "assistant_mcp_route_candidate_v1",
|
||||
policy_owner: "assistantMcpDiscoveryRuntimeBridge",
|
||||
candidate_status: "ready_for_reviewed_execution",
|
||||
selected_chain_id: "value_flow_ranking",
|
||||
selected_chain_summary: "Rank value flow",
|
||||
nearest_catalog_chain_template: "value_flow_ranking",
|
||||
selected_chain_id: "value_flow",
|
||||
selected_chain_summary: "Measure value flow",
|
||||
nearest_catalog_chain_template: "value_flow",
|
||||
catalog_alignment_status: "selected_matches_top",
|
||||
business_fact_family: "value_flow",
|
||||
action_family: "turnover",
|
||||
|
|
@ -63,6 +63,22 @@ function entryPointContract(overrides: Record<string, unknown> = {}) {
|
|||
recommended_next_action: "Execute through the reviewed runtime bridge and truth gate.",
|
||||
forbidden_overclaim_flags: ["no_unchecked_fact_totals"]
|
||||
},
|
||||
execution_handoff: {
|
||||
schema_version: "assistant_mcp_discovery_execution_handoff_v1",
|
||||
policy_owner: "assistantMcpDiscoveryExecutionHandoff",
|
||||
handoff_status: "ready_for_guarded_response",
|
||||
selected_chain_id: "value_flow",
|
||||
route_candidate_status: "ready_for_reviewed_execution",
|
||||
evidence_answer_mode: "confirmed_business_answer",
|
||||
evidence_expected_coverage: "confirmed_coverage",
|
||||
pilot_status: "executed",
|
||||
answer_mode: "confirmed_with_bounded_inference",
|
||||
mcp_execution_performed: true,
|
||||
allowed_hot_chain: true,
|
||||
can_use_guarded_response: true,
|
||||
must_keep_internal_mechanics_hidden: true,
|
||||
reason_codes: ["execution_handoff_guarded_response_ready"]
|
||||
},
|
||||
answer_draft: {
|
||||
answer_mode: "confirmed_with_bounded_inference"
|
||||
}
|
||||
|
|
@ -86,14 +102,14 @@ describe("assistant MCP discovery debug attachment", () => {
|
|||
expect(debug.mcp_discovery_attempted).toBe(true);
|
||||
expect(debug.mcp_discovery_hot_runtime_wired).toBe(false);
|
||||
expect(debug.mcp_discovery_bridge_status).toBe("answer_draft_ready");
|
||||
expect(debug.mcp_discovery_selected_chain_id).toBe("value_flow_ranking");
|
||||
expect(debug.mcp_discovery_selected_chain_id).toBe("value_flow");
|
||||
expect(debug.mcp_discovery_evidence_plan_status).toBe("ready_for_execution");
|
||||
expect(debug.mcp_discovery_evidence_plan_answer_mode).toBe("confirmed_business_answer");
|
||||
expect(debug.mcp_discovery_evidence_plan_expected_coverage).toBe("confirmed_coverage");
|
||||
expect(debug.mcp_discovery_evidence_plan_missing_axes).toEqual([]);
|
||||
expect(debug.mcp_discovery_catalog_chain_template_matches).toEqual(["value_flow_ranking", "value_flow"]);
|
||||
expect(debug.mcp_discovery_catalog_chain_template_matches).toEqual(["value_flow"]);
|
||||
expect(debug.mcp_discovery_catalog_chain_alignment_status).toBe("selected_matches_top");
|
||||
expect(debug.mcp_discovery_catalog_chain_top_match).toBe("value_flow_ranking");
|
||||
expect(debug.mcp_discovery_catalog_chain_top_match).toBe("value_flow");
|
||||
expect(debug.mcp_discovery_catalog_chain_selected_matches_top).toBe(true);
|
||||
expect(debug.mcp_discovery_route_candidate_status).toBe("ready_for_reviewed_execution");
|
||||
expect(debug.mcp_discovery_route_candidate_fact_family).toBe("value_flow");
|
||||
|
|
@ -107,6 +123,9 @@ describe("assistant MCP discovery debug attachment", () => {
|
|||
expect(debug.mcp_discovery_route_candidate_next_action).toBe(
|
||||
"Execute through the reviewed runtime bridge and truth gate."
|
||||
);
|
||||
expect(debug.mcp_discovery_execution_handoff_status).toBe("ready_for_guarded_response");
|
||||
expect(debug.mcp_discovery_execution_handoff_allowed_hot_chain).toBe(true);
|
||||
expect(debug.mcp_discovery_execution_handoff_can_use_guarded_response).toBe(true);
|
||||
expect(debug.mcp_discovery_answer_mode).toBe("confirmed_with_bounded_inference");
|
||||
expect(debug.mcp_discovery_business_fact_answer_allowed).toBe(true);
|
||||
expect(debug.mcp_discovery_user_facing_response_allowed).toBe(true);
|
||||
|
|
@ -142,6 +161,10 @@ describe("assistant MCP discovery debug attachment", () => {
|
|||
expect(debug.mcp_discovery_route_candidate_missing_axes).toEqual([]);
|
||||
expect(debug.mcp_discovery_route_candidate_provided_axes).toEqual([]);
|
||||
expect(debug.mcp_discovery_route_candidate_executable_now).toBe(false);
|
||||
expect(debug.mcp_discovery_execution_handoff_v1).toBeNull();
|
||||
expect(debug.mcp_discovery_execution_handoff_status).toBeNull();
|
||||
expect(debug.mcp_discovery_execution_handoff_allowed_hot_chain).toBe(false);
|
||||
expect(debug.mcp_discovery_execution_handoff_can_use_guarded_response).toBe(false);
|
||||
expect(debug.mcp_discovery_answer_mode).toBeNull();
|
||||
expect(debug.mcp_discovery_business_fact_answer_allowed).toBe(false);
|
||||
expect(debug.mcp_discovery_user_facing_response_allowed).toBe(false);
|
||||
|
|
|
|||
|
|
@ -71,6 +71,11 @@ describe("assistant MCP discovery runtime bridge", () => {
|
|||
expect(result.answer_draft.answer_mode).toBe("confirmed_with_bounded_inference");
|
||||
expect(result.business_fact_answer_allowed).toBe(true);
|
||||
expect(result.user_facing_response_allowed).toBe(true);
|
||||
expect(result.execution_handoff).toMatchObject({
|
||||
handoff_status: "not_enabled_for_chain",
|
||||
allowed_hot_chain: false,
|
||||
can_use_guarded_response: false
|
||||
});
|
||||
expect(result.reason_codes).toContain("runtime_bridge_not_wired_to_hot_assistant_answer");
|
||||
});
|
||||
|
||||
|
|
@ -88,6 +93,10 @@ describe("assistant MCP discovery runtime bridge", () => {
|
|||
expect(result.requires_user_clarification).toBe(true);
|
||||
expect(result.pilot.mcp_execution_performed).toBe(false);
|
||||
expect(result.business_fact_answer_allowed).toBe(false);
|
||||
expect(result.execution_handoff).toMatchObject({
|
||||
handoff_status: "awaiting_user_scope",
|
||||
can_use_guarded_response: false
|
||||
});
|
||||
expect(result.answer_draft.next_step_line).toContain("Уточните контрагента");
|
||||
});
|
||||
|
||||
|
|
@ -242,6 +251,11 @@ describe("assistant MCP discovery runtime bridge", () => {
|
|||
executable_now: true,
|
||||
enablement_reason: null
|
||||
});
|
||||
expect(result.execution_handoff).toMatchObject({
|
||||
handoff_status: "not_enabled_for_chain",
|
||||
allowed_hot_chain: false,
|
||||
can_use_guarded_response: false
|
||||
});
|
||||
expect(result.answer_draft.confirmed_lines.join("\n")).toContain("СВК-А");
|
||||
});
|
||||
|
||||
|
|
@ -1065,6 +1079,19 @@ describe("assistant MCP discovery runtime bridge", () => {
|
|||
period_scope: "2020",
|
||||
total_amount: 5000
|
||||
});
|
||||
expect(result.execution_handoff).toMatchObject({
|
||||
schema_version: "assistant_mcp_discovery_execution_handoff_v1",
|
||||
handoff_status: "ready_for_guarded_response",
|
||||
selected_chain_id: "value_flow",
|
||||
route_candidate_status: "ready_for_reviewed_execution",
|
||||
pilot_status: "executed",
|
||||
mcp_execution_performed: true,
|
||||
allowed_hot_chain: true,
|
||||
can_use_guarded_response: true,
|
||||
must_keep_internal_mechanics_hidden: true
|
||||
});
|
||||
expect(result.reason_codes).toContain("runtime_bridge_execution_handoff_ready_for_guarded_response");
|
||||
expect(result.reason_codes).toContain("execution_handoff_guarded_response_ready");
|
||||
expect(result.answer_draft.confirmed_lines.join("\n")).toContain("5 000");
|
||||
expect(result.answer_draft.confirmed_lines.join("\n")).not.toContain("контрагенту");
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue