ARCH: добавить policy gate ответа MCP discovery

This commit is contained in:
2026-04-20 12:06:22 +03:00
parent 58c1358960
commit c744308223
7 changed files with 515 additions and 2 deletions
@@ -169,6 +169,66 @@ describe("assistant living chat runtime adapter", () => {
expect(executeLlmChat).not.toHaveBeenCalled();
});
it("replaces unsupported boundary with guarded MCP discovery response when policy allows it", async () => {
const executeLlmChat = vi.fn(async () => "raw-llm");
const input = buildRuntimeInput({
userMessage: "how long has svk been active",
modeDecision: { mode: "chat", reason: "unsupported_current_turn_meaning_boundary" },
addressRuntimeMeta: {
toolGateReason: "unsupported_current_turn_meaning_boundary",
orchestrationContract: {
unsupported_current_turn_meaning_boundary: true,
assistant_turn_meaning: {
unsupported_but_understood_family: "counterparty_lifecycle_or_age",
explicit_entity_candidates: [
{
type: "counterparty",
value: "SVK"
}
]
}
},
mcpDiscoveryRuntimeEntryPoint: {
schema_version: "assistant_mcp_discovery_runtime_entry_point_v1",
policy_owner: "assistantMcpDiscoveryRuntimeEntryPoint",
entry_status: "bridge_executed",
hot_runtime_wired: false,
discovery_attempted: true,
turn_input: { adapter_status: "ready" },
bridge: {
bridge_status: "answer_draft_ready",
user_facing_response_allowed: true,
business_fact_answer_allowed: true,
requires_user_clarification: false,
answer_draft: {
answer_mode: "confirmed_with_bounded_inference",
headline: "Confirmed scoped answer.",
confirmed_lines: ["Confirmed fact"],
inference_lines: ["Bounded inference"],
unknown_lines: ["Unconfirmed legal fact"],
limitation_lines: ["Limited source window"],
next_step_line: null
}
},
reason_codes: ["runtime_entry_point_bridge_executed"]
}
},
executeLlmChat
});
const output = await runAssistantLivingChatRuntime(input);
expect(output.handled).toBe(true);
expect(output.chatText).toContain("Confirmed fact");
expect(output.chatText).not.toContain("route");
expect(output.chatText).not.toContain("query_documents");
expect(output.debug?.living_chat_response_source).toBe("mcp_discovery_response_candidate_guarded");
expect(output.debug?.mcp_discovery_response_applied).toBe(true);
expect(output.debug?.mcp_discovery_entry_status).toBe("bridge_executed");
expect(output.debug?.mcp_discovery_attempted).toBe(true);
expect(executeLlmChat).not.toHaveBeenCalled();
});
it("adds proactive organization offer on first smalltalk turn when multiple organizations are available", async () => {
const resolveDataScopeProbe = vi.fn(async () => ({
status: "resolved",
@@ -0,0 +1,91 @@
import { describe, expect, it } from "vitest";
import { applyAssistantMcpDiscoveryResponsePolicy } from "../src/services/assistantMcpDiscoveryResponsePolicy";
function entryPoint(overrides: Record<string, unknown> = {}) {
return {
schema_version: "assistant_mcp_discovery_runtime_entry_point_v1",
policy_owner: "assistantMcpDiscoveryRuntimeEntryPoint",
entry_status: "bridge_executed",
hot_runtime_wired: false,
discovery_attempted: true,
turn_input: { adapter_status: "ready" },
bridge: {
bridge_status: "answer_draft_ready",
user_facing_response_allowed: true,
business_fact_answer_allowed: true,
requires_user_clarification: false,
answer_draft: {
answer_mode: "confirmed_with_bounded_inference",
headline: "Confirmed scoped answer.",
confirmed_lines: ["Confirmed fact"],
inference_lines: ["Bounded inference"],
unknown_lines: ["Unconfirmed fact"],
limitation_lines: ["Limited source window"],
next_step_line: null
}
},
reason_codes: ["runtime_entry_point_bridge_executed"],
...overrides
} as any;
}
describe("assistant MCP discovery response policy", () => {
it("applies a guarded candidate only for unsupported current-turn boundary replies", () => {
const result = applyAssistantMcpDiscoveryResponsePolicy({
currentReply: "route is not wired",
currentReplySource: "deterministic_unsupported_current_turn_boundary",
modeDecisionReason: "unsupported_current_turn_meaning_boundary",
addressRuntimeMeta: {
mcpDiscoveryRuntimeEntryPoint: entryPoint()
}
});
expect(result.applied).toBe(true);
expect(result.decision).toBe("apply_candidate");
expect(result.reply_source).toBe("mcp_discovery_response_candidate_guarded");
expect(result.reply_text).toContain("Confirmed fact");
expect(result.reply_text).not.toContain("query_documents");
expect(result.reason_codes).toContain("mcp_discovery_response_policy_candidate_applied");
});
it("keeps the current reply when the turn is not an unsupported boundary", () => {
const result = applyAssistantMcpDiscoveryResponsePolicy({
currentReply: "regular chat",
currentReplySource: "llm_chat",
modeDecisionReason: "living_chat_signal_detected",
addressRuntimeMeta: {
mcpDiscoveryRuntimeEntryPoint: entryPoint()
}
});
expect(result.applied).toBe(false);
expect(result.decision).toBe("keep_current_reply");
expect(result.reply_text).toBe("regular chat");
expect(result.reply_source).toBe("llm_chat");
expect(result.reason_codes).toContain("mcp_discovery_response_policy_not_unsupported_boundary");
});
it("keeps the current reply when the candidate has no grounded text", () => {
const result = applyAssistantMcpDiscoveryResponsePolicy({
currentReply: "route is not wired",
currentReplySource: "deterministic_unsupported_current_turn_boundary",
modeDecisionReason: "unsupported_current_turn_meaning_boundary",
addressRuntimeMeta: {
mcpDiscoveryRuntimeEntryPoint: entryPoint({
bridge: {
bridge_status: "unsupported",
user_facing_response_allowed: true,
business_fact_answer_allowed: false,
requires_user_clarification: false,
answer_draft: null
}
})
}
});
expect(result.applied).toBe(false);
expect(result.reply_text).toBe("route is not wired");
expect(result.reason_codes).toContain("mcp_discovery_response_policy_candidate_not_eligible");
expect(result.reason_codes).toContain("mcp_discovery_response_policy_kept_current_reply");
});
});