Протянуть hot handoff в discovery response

This commit is contained in:
dctouch 2026-05-22 19:33:02 +03:00
parent 5125c747d8
commit e7603a9d29
10 changed files with 111 additions and 16 deletions

View File

@ -1109,6 +1109,14 @@ function statusFrom(entryPoint) {
} }
return "not_applicable"; return "not_applicable";
} }
function hasGuardedHotHandoff(entryPoint) {
const bridge = toRecordObject(entryPoint?.bridge);
const handoff = toRecordObject(bridge?.execution_handoff);
return (entryPoint?.hot_runtime_wired === true &&
bridge?.hot_runtime_wired === true &&
handoff?.can_use_guarded_response === true &&
handoff?.must_keep_internal_mechanics_hidden === true);
}
function replyTypeFor(status) { function replyTypeFor(status) {
if (status === "clarification_candidate") { if (status === "clarification_candidate") {
return "clarification_required"; return "clarification_required";
@ -1149,9 +1157,12 @@ function buildReplyText(entryPoint, status) {
function buildAssistantMcpDiscoveryResponseCandidate(entryPoint) { function buildAssistantMcpDiscoveryResponseCandidate(entryPoint) {
const entry = entryPoint ?? null; const entry = entryPoint ?? null;
const status = statusFrom(entry); const status = statusFrom(entry);
const guardedHotHandoff = hasGuardedHotHandoff(entry);
const reasonCodes = uniqueStrings(entry?.reason_codes ?? []); const reasonCodes = uniqueStrings(entry?.reason_codes ?? []);
pushReason(reasonCodes, `mcp_discovery_response_candidate_${status}`); pushReason(reasonCodes, `mcp_discovery_response_candidate_${status}`);
pushReason(reasonCodes, "mcp_discovery_response_candidate_not_hot_wired"); pushReason(reasonCodes, guardedHotHandoff
? "mcp_discovery_response_candidate_guarded_hot_wired"
: "mcp_discovery_response_candidate_not_hot_wired");
const replyText = entry && (status === "ready_for_guarded_use" || status === "checked_sources_only_candidate" || status === "clarification_candidate") const replyText = entry && (status === "ready_for_guarded_use" || status === "checked_sources_only_candidate" || status === "clarification_candidate")
? buildReplyText(entry, status) ? buildReplyText(entry, status)
: null; : null;
@ -1159,10 +1170,11 @@ function buildAssistantMcpDiscoveryResponseCandidate(entryPoint) {
schema_version: exports.ASSISTANT_MCP_DISCOVERY_RESPONSE_CANDIDATE_SCHEMA_VERSION, schema_version: exports.ASSISTANT_MCP_DISCOVERY_RESPONSE_CANDIDATE_SCHEMA_VERSION,
policy_owner: "assistantMcpDiscoveryResponseCandidate", policy_owner: "assistantMcpDiscoveryResponseCandidate",
candidate_status: replyText ? status : status === "clarification_candidate" ? status : status, candidate_status: replyText ? status : status === "clarification_candidate" ? status : status,
hot_runtime_wired: false, hot_runtime_wired: guardedHotHandoff,
reply_type: replyTypeFor(status), reply_type: replyTypeFor(status),
reply_text: replyText, reply_text: replyText,
eligible_for_future_hot_runtime: Boolean(replyText) && (status === "ready_for_guarded_use" || status === "checked_sources_only_candidate" || status === "clarification_candidate"), eligible_for_future_hot_runtime: Boolean(replyText) &&
(status === "ready_for_guarded_use" || status === "checked_sources_only_candidate" || status === "clarification_candidate"),
must_keep_internal_mechanics_hidden: true, must_keep_internal_mechanics_hidden: true,
reason_codes: reasonCodes reason_codes: reasonCodes
}; };

View File

@ -675,6 +675,9 @@ function applyAssistantMcpDiscoveryResponsePolicy(input) {
if (!candidate.eligible_for_future_hot_runtime) { if (!candidate.eligible_for_future_hot_runtime) {
pushReason(reasonCodes, "mcp_discovery_response_policy_candidate_not_eligible"); pushReason(reasonCodes, "mcp_discovery_response_policy_candidate_not_eligible");
} }
if (candidate.hot_runtime_wired) {
pushReason(reasonCodes, "mcp_discovery_response_policy_candidate_guarded_hot_wired");
}
if (!toNonEmptyString(candidate.reply_text)) { if (!toNonEmptyString(candidate.reply_text)) {
pushReason(reasonCodes, "mcp_discovery_response_policy_candidate_missing_reply_text"); pushReason(reasonCodes, "mcp_discovery_response_policy_candidate_missing_reply_text");
} }

View File

@ -283,7 +283,7 @@ async function runAssistantMcpDiscoveryRuntimeBridge(input) {
schema_version: exports.ASSISTANT_MCP_DISCOVERY_RUNTIME_BRIDGE_SCHEMA_VERSION, schema_version: exports.ASSISTANT_MCP_DISCOVERY_RUNTIME_BRIDGE_SCHEMA_VERSION,
policy_owner: "assistantMcpDiscoveryRuntimeBridge", policy_owner: "assistantMcpDiscoveryRuntimeBridge",
bridge_status: bridgeStatus, bridge_status: bridgeStatus,
hot_runtime_wired: false, hot_runtime_wired: executionHandoff.can_use_guarded_response,
planner, planner,
pilot, pilot,
answer_draft: answerDraft, answer_draft: answerDraft,

View File

@ -69,12 +69,14 @@ async function runAssistantMcpDiscoveryRuntimeEntryPoint(input) {
}); });
const reasonCodes = uniqueStrings([...turnInput.reason_codes, ...bridge.reason_codes]); const reasonCodes = uniqueStrings([...turnInput.reason_codes, ...bridge.reason_codes]);
pushReason(reasonCodes, "runtime_entry_point_bridge_executed"); pushReason(reasonCodes, "runtime_entry_point_bridge_executed");
pushReason(reasonCodes, "runtime_entry_point_not_wired_to_hot_assistant_answer"); pushReason(reasonCodes, bridge.hot_runtime_wired
? "runtime_entry_point_wired_to_guarded_hot_assistant_answer"
: "runtime_entry_point_not_wired_to_hot_assistant_answer");
return { return {
schema_version: exports.ASSISTANT_MCP_DISCOVERY_RUNTIME_ENTRY_POINT_SCHEMA_VERSION, schema_version: exports.ASSISTANT_MCP_DISCOVERY_RUNTIME_ENTRY_POINT_SCHEMA_VERSION,
policy_owner: "assistantMcpDiscoveryRuntimeEntryPoint", policy_owner: "assistantMcpDiscoveryRuntimeEntryPoint",
entry_status: "bridge_executed", entry_status: "bridge_executed",
hot_runtime_wired: false, hot_runtime_wired: bridge.hot_runtime_wired,
discovery_attempted: true, discovery_attempted: true,
turn_input: turnInput, turn_input: turnInput,
bridge, bridge,

View File

@ -16,7 +16,7 @@ export interface AssistantMcpDiscoveryResponseCandidateContract {
schema_version: typeof ASSISTANT_MCP_DISCOVERY_RESPONSE_CANDIDATE_SCHEMA_VERSION; schema_version: typeof ASSISTANT_MCP_DISCOVERY_RESPONSE_CANDIDATE_SCHEMA_VERSION;
policy_owner: "assistantMcpDiscoveryResponseCandidate"; policy_owner: "assistantMcpDiscoveryResponseCandidate";
candidate_status: AssistantMcpDiscoveryResponseCandidateStatus; candidate_status: AssistantMcpDiscoveryResponseCandidateStatus;
hot_runtime_wired: false; hot_runtime_wired: boolean;
reply_type: "partial_coverage" | "clarification_required" | "no_grounded_answer"; reply_type: "partial_coverage" | "clarification_required" | "no_grounded_answer";
reply_text: string | null; reply_text: string | null;
eligible_for_future_hot_runtime: boolean; eligible_for_future_hot_runtime: boolean;
@ -1334,6 +1334,17 @@ function statusFrom(entryPoint: AssistantMcpDiscoveryRuntimeEntryPointContract |
return "not_applicable"; return "not_applicable";
} }
function hasGuardedHotHandoff(entryPoint: AssistantMcpDiscoveryRuntimeEntryPointContract | null): boolean {
const bridge = toRecordObject(entryPoint?.bridge);
const handoff = toRecordObject(bridge?.execution_handoff);
return (
entryPoint?.hot_runtime_wired === true &&
bridge?.hot_runtime_wired === true &&
handoff?.can_use_guarded_response === true &&
handoff?.must_keep_internal_mechanics_hidden === true
);
}
function replyTypeFor( function replyTypeFor(
status: AssistantMcpDiscoveryResponseCandidateStatus status: AssistantMcpDiscoveryResponseCandidateStatus
): AssistantMcpDiscoveryResponseCandidateContract["reply_type"] { ): AssistantMcpDiscoveryResponseCandidateContract["reply_type"] {
@ -1384,9 +1395,15 @@ export function buildAssistantMcpDiscoveryResponseCandidate(
): AssistantMcpDiscoveryResponseCandidateContract { ): AssistantMcpDiscoveryResponseCandidateContract {
const entry = entryPoint ?? null; const entry = entryPoint ?? null;
const status = statusFrom(entry); const status = statusFrom(entry);
const guardedHotHandoff = hasGuardedHotHandoff(entry);
const reasonCodes = uniqueStrings(entry?.reason_codes ?? []); const reasonCodes = uniqueStrings(entry?.reason_codes ?? []);
pushReason(reasonCodes, `mcp_discovery_response_candidate_${status}`); pushReason(reasonCodes, `mcp_discovery_response_candidate_${status}`);
pushReason(reasonCodes, "mcp_discovery_response_candidate_not_hot_wired"); pushReason(
reasonCodes,
guardedHotHandoff
? "mcp_discovery_response_candidate_guarded_hot_wired"
: "mcp_discovery_response_candidate_not_hot_wired"
);
const replyText = const replyText =
entry && (status === "ready_for_guarded_use" || status === "checked_sources_only_candidate" || status === "clarification_candidate") entry && (status === "ready_for_guarded_use" || status === "checked_sources_only_candidate" || status === "clarification_candidate")
@ -1397,11 +1414,12 @@ export function buildAssistantMcpDiscoveryResponseCandidate(
schema_version: ASSISTANT_MCP_DISCOVERY_RESPONSE_CANDIDATE_SCHEMA_VERSION, schema_version: ASSISTANT_MCP_DISCOVERY_RESPONSE_CANDIDATE_SCHEMA_VERSION,
policy_owner: "assistantMcpDiscoveryResponseCandidate", policy_owner: "assistantMcpDiscoveryResponseCandidate",
candidate_status: replyText ? status : status === "clarification_candidate" ? status : status, candidate_status: replyText ? status : status === "clarification_candidate" ? status : status,
hot_runtime_wired: false, hot_runtime_wired: guardedHotHandoff,
reply_type: replyTypeFor(status), reply_type: replyTypeFor(status),
reply_text: replyText, reply_text: replyText,
eligible_for_future_hot_runtime: eligible_for_future_hot_runtime:
Boolean(replyText) && (status === "ready_for_guarded_use" || status === "checked_sources_only_candidate" || status === "clarification_candidate"), Boolean(replyText) &&
(status === "ready_for_guarded_use" || status === "checked_sources_only_candidate" || status === "clarification_candidate"),
must_keep_internal_mechanics_hidden: true, must_keep_internal_mechanics_hidden: true,
reason_codes: reasonCodes reason_codes: reasonCodes
}; };

View File

@ -910,6 +910,9 @@ export function applyAssistantMcpDiscoveryResponsePolicy(
if (!candidate.eligible_for_future_hot_runtime) { if (!candidate.eligible_for_future_hot_runtime) {
pushReason(reasonCodes, "mcp_discovery_response_policy_candidate_not_eligible"); pushReason(reasonCodes, "mcp_discovery_response_policy_candidate_not_eligible");
} }
if (candidate.hot_runtime_wired) {
pushReason(reasonCodes, "mcp_discovery_response_policy_candidate_guarded_hot_wired");
}
if (!toNonEmptyString(candidate.reply_text)) { if (!toNonEmptyString(candidate.reply_text)) {
pushReason(reasonCodes, "mcp_discovery_response_policy_candidate_missing_reply_text"); pushReason(reasonCodes, "mcp_discovery_response_policy_candidate_missing_reply_text");
} }

View File

@ -99,7 +99,7 @@ export interface AssistantMcpDiscoveryRuntimeBridgeContract {
schema_version: typeof ASSISTANT_MCP_DISCOVERY_RUNTIME_BRIDGE_SCHEMA_VERSION; schema_version: typeof ASSISTANT_MCP_DISCOVERY_RUNTIME_BRIDGE_SCHEMA_VERSION;
policy_owner: "assistantMcpDiscoveryRuntimeBridge"; policy_owner: "assistantMcpDiscoveryRuntimeBridge";
bridge_status: AssistantMcpDiscoveryRuntimeBridgeStatus; bridge_status: AssistantMcpDiscoveryRuntimeBridgeStatus;
hot_runtime_wired: false; hot_runtime_wired: boolean;
planner: AssistantMcpDiscoveryPlannerContract; planner: AssistantMcpDiscoveryPlannerContract;
pilot: AssistantMcpDiscoveryPilotExecutionContract; pilot: AssistantMcpDiscoveryPilotExecutionContract;
answer_draft: AssistantMcpDiscoveryAnswerDraftContract; answer_draft: AssistantMcpDiscoveryAnswerDraftContract;
@ -445,7 +445,7 @@ export async function runAssistantMcpDiscoveryRuntimeBridge(
schema_version: ASSISTANT_MCP_DISCOVERY_RUNTIME_BRIDGE_SCHEMA_VERSION, schema_version: ASSISTANT_MCP_DISCOVERY_RUNTIME_BRIDGE_SCHEMA_VERSION,
policy_owner: "assistantMcpDiscoveryRuntimeBridge", policy_owner: "assistantMcpDiscoveryRuntimeBridge",
bridge_status: bridgeStatus, bridge_status: bridgeStatus,
hot_runtime_wired: false, hot_runtime_wired: executionHandoff.can_use_guarded_response,
planner, planner,
pilot, pilot,
answer_draft: answerDraft, answer_draft: answerDraft,

View File

@ -26,7 +26,7 @@ export interface AssistantMcpDiscoveryRuntimeEntryPointContract {
schema_version: typeof ASSISTANT_MCP_DISCOVERY_RUNTIME_ENTRY_POINT_SCHEMA_VERSION; schema_version: typeof ASSISTANT_MCP_DISCOVERY_RUNTIME_ENTRY_POINT_SCHEMA_VERSION;
policy_owner: "assistantMcpDiscoveryRuntimeEntryPoint"; policy_owner: "assistantMcpDiscoveryRuntimeEntryPoint";
entry_status: AssistantMcpDiscoveryRuntimeEntryPointStatus; entry_status: AssistantMcpDiscoveryRuntimeEntryPointStatus;
hot_runtime_wired: false; hot_runtime_wired: boolean;
discovery_attempted: boolean; discovery_attempted: boolean;
turn_input: AssistantMcpDiscoveryTurnInputContract; turn_input: AssistantMcpDiscoveryTurnInputContract;
bridge: AssistantMcpDiscoveryRuntimeBridgeContract | null; bridge: AssistantMcpDiscoveryRuntimeBridgeContract | null;
@ -108,13 +108,18 @@ export async function runAssistantMcpDiscoveryRuntimeEntryPoint(
}); });
const reasonCodes = uniqueStrings([...turnInput.reason_codes, ...bridge.reason_codes]); const reasonCodes = uniqueStrings([...turnInput.reason_codes, ...bridge.reason_codes]);
pushReason(reasonCodes, "runtime_entry_point_bridge_executed"); pushReason(reasonCodes, "runtime_entry_point_bridge_executed");
pushReason(reasonCodes, "runtime_entry_point_not_wired_to_hot_assistant_answer"); pushReason(
reasonCodes,
bridge.hot_runtime_wired
? "runtime_entry_point_wired_to_guarded_hot_assistant_answer"
: "runtime_entry_point_not_wired_to_hot_assistant_answer"
);
return { return {
schema_version: ASSISTANT_MCP_DISCOVERY_RUNTIME_ENTRY_POINT_SCHEMA_VERSION, schema_version: ASSISTANT_MCP_DISCOVERY_RUNTIME_ENTRY_POINT_SCHEMA_VERSION,
policy_owner: "assistantMcpDiscoveryRuntimeEntryPoint", policy_owner: "assistantMcpDiscoveryRuntimeEntryPoint",
entry_status: "bridge_executed", entry_status: "bridge_executed",
hot_runtime_wired: false, hot_runtime_wired: bridge.hot_runtime_wired,
discovery_attempted: true, discovery_attempted: true,
turn_input: turnInput, turn_input: turnInput,
bridge, bridge,

View File

@ -29,6 +29,45 @@ function entryPoint(overrides: Record<string, unknown> = {}) {
} as any; } as any;
} }
function hotValueFlowEntryPoint(overrides: Record<string, unknown> = {}) {
const base = entryPoint({
hot_runtime_wired: true,
bridge: {
bridge_status: "answer_draft_ready",
hot_runtime_wired: true,
user_facing_response_allowed: true,
business_fact_answer_allowed: true,
requires_user_clarification: false,
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",
headline: "Confirmed value flow.",
confirmed_lines: ["1C value-flow rows were found for counterparty SVK", "Confirmed scoped amount: 3 750 руб."],
inference_lines: ["Counterparty value-flow total was calculated from confirmed 1C movement rows"],
unknown_lines: [],
limitation_lines: [],
next_step_line: null
}
}
});
return { ...base, ...overrides } as any;
}
describe("assistant MCP discovery response candidate", () => { describe("assistant MCP discovery response candidate", () => {
it("builds a Russian guarded candidate from a confirmed discovery draft", () => { it("builds a Russian guarded candidate from a confirmed discovery draft", () => {
const candidate = buildAssistantMcpDiscoveryResponseCandidate(entryPoint()); const candidate = buildAssistantMcpDiscoveryResponseCandidate(entryPoint());
@ -44,6 +83,16 @@ describe("assistant MCP discovery response candidate", () => {
expect(candidate.reply_text).not.toContain("primitive"); expect(candidate.reply_text).not.toContain("primitive");
}); });
it("marks an allowlisted value-flow handoff as hot-wired for guarded runtime use", () => {
const candidate = buildAssistantMcpDiscoveryResponseCandidate(hotValueFlowEntryPoint());
expect(candidate.candidate_status).toBe("ready_for_guarded_use");
expect(candidate.hot_runtime_wired).toBe(true);
expect(candidate.eligible_for_future_hot_runtime).toBe(true);
expect(candidate.reason_codes).toContain("mcp_discovery_response_candidate_guarded_hot_wired");
expect(candidate.reply_text).toContain("3 750");
});
it("keeps inventory reserve boundary answers direct instead of compacting into a money overview", () => { it("keeps inventory reserve boundary answers direct instead of compacting into a money overview", () => {
const candidate = buildAssistantMcpDiscoveryResponseCandidate( const candidate = buildAssistantMcpDiscoveryResponseCandidate(
entryPoint({ entryPoint({

View File

@ -126,7 +126,10 @@ describe("assistant MCP discovery runtime entry point", () => {
expect(result.bridge?.bridge_status).toBe("answer_draft_ready"); expect(result.bridge?.bridge_status).toBe("answer_draft_ready");
expect(result.bridge?.pilot.pilot_scope).toBe("counterparty_value_flow_query_movements_v1"); expect(result.bridge?.pilot.pilot_scope).toBe("counterparty_value_flow_query_movements_v1");
expect(result.bridge?.pilot.derived_value_flow?.total_amount).toBe(3750); expect(result.bridge?.pilot.derived_value_flow?.total_amount).toBe(3750);
expect(result.bridge?.hot_runtime_wired).toBe(false); expect(result.bridge?.hot_runtime_wired).toBe(true);
expect(result.bridge?.execution_handoff.can_use_guarded_response).toBe(true);
expect(result.hot_runtime_wired).toBe(true);
expect(result.reason_codes).toContain("runtime_entry_point_wired_to_guarded_hot_assistant_answer");
expect(result.reason_codes).toContain("mcp_discovery_unsupported_but_understood_turn"); expect(result.reason_codes).toContain("mcp_discovery_unsupported_but_understood_turn");
}); });