Planner Autonomy: протащить catalog chain matches в runtime debug

This commit is contained in:
2026-05-01 14:15:55 +03:00
parent 52a974d4d7
commit 1c1383b8b5
8 changed files with 60 additions and 4 deletions
@@ -15,6 +15,19 @@ function toNonEmptyString(value) {
const text = String(value).trim();
return text.length > 0 ? text : null;
}
function toStringArray(value) {
if (!Array.isArray(value)) {
return [];
}
const result = [];
for (const item of value) {
const text = toNonEmptyString(item);
if (text && !result.includes(text)) {
result.push(text);
}
}
return result;
}
function isMcpDiscoveryEntryPointContract(value) {
const record = toRecordObject(value);
return (record?.schema_version === "assistant_mcp_discovery_runtime_entry_point_v1" &&
@@ -32,6 +45,7 @@ function resolveEntryPoint(input) {
function buildAssistantMcpDiscoveryDebugAttachmentFields(input) {
const entryPoint = resolveEntryPoint(input);
const bridge = toRecordObject(entryPoint?.bridge);
const planner = toRecordObject(bridge?.planner);
const answerDraft = toRecordObject(bridge?.answer_draft);
return {
assistant_mcp_discovery_entry_point_v1: entryPoint,
@@ -39,6 +53,8 @@ function buildAssistantMcpDiscoveryDebugAttachmentFields(input) {
mcp_discovery_attempted: Boolean(entryPoint?.discovery_attempted),
mcp_discovery_hot_runtime_wired: false,
mcp_discovery_bridge_status: toNonEmptyString(bridge?.bridge_status),
mcp_discovery_selected_chain_id: toNonEmptyString(planner?.selected_chain_id),
mcp_discovery_catalog_chain_template_matches: toStringArray(planner?.catalog_chain_template_matches),
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,
@@ -90,6 +90,7 @@ function buildLoopState(planner, pilot, bridgeStatus) {
policy_owner: "assistantMcpDiscoveryRuntimeBridge",
loop_status: loopStatusFor(bridgeStatus),
selected_chain_id: planner.selected_chain_id,
catalog_chain_template_matches: [...planner.catalog_chain_template_matches],
pilot_scope: pilot.pilot_scope,
asked_domain_family: planner.discovery_plan.turn_meaning_ref?.asked_domain_family ?? null,
asked_action_family: planner.discovery_plan.turn_meaning_ref?.asked_action_family ?? null,
@@ -6,6 +6,8 @@ export interface AssistantMcpDiscoveryDebugAttachmentFields {
mcp_discovery_attempted: boolean;
mcp_discovery_hot_runtime_wired: false;
mcp_discovery_bridge_status: string | null;
mcp_discovery_selected_chain_id: string | null;
mcp_discovery_catalog_chain_template_matches: string[];
mcp_discovery_answer_mode: string | null;
mcp_discovery_business_fact_answer_allowed: boolean;
mcp_discovery_user_facing_response_allowed: boolean;
@@ -32,6 +34,20 @@ function toNonEmptyString(value: unknown): string | null {
return text.length > 0 ? text : null;
}
function toStringArray(value: unknown): string[] {
if (!Array.isArray(value)) {
return [];
}
const result: string[] = [];
for (const item of value) {
const text = toNonEmptyString(item);
if (text && !result.includes(text)) {
result.push(text);
}
}
return result;
}
function isMcpDiscoveryEntryPointContract(value: unknown): value is AssistantMcpDiscoveryRuntimeEntryPointContract {
const record = toRecordObject(value);
return (
@@ -56,6 +72,7 @@ export function buildAssistantMcpDiscoveryDebugAttachmentFields(
): AssistantMcpDiscoveryDebugAttachmentFields {
const entryPoint = resolveEntryPoint(input);
const bridge = toRecordObject(entryPoint?.bridge);
const planner = toRecordObject(bridge?.planner);
const answerDraft = toRecordObject(bridge?.answer_draft);
return {
@@ -64,6 +81,8 @@ export function buildAssistantMcpDiscoveryDebugAttachmentFields(
mcp_discovery_attempted: Boolean(entryPoint?.discovery_attempted),
mcp_discovery_hot_runtime_wired: false,
mcp_discovery_bridge_status: toNonEmptyString(bridge?.bridge_status),
mcp_discovery_selected_chain_id: toNonEmptyString(planner?.selected_chain_id),
mcp_discovery_catalog_chain_template_matches: toStringArray(planner?.catalog_chain_template_matches),
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,
@@ -45,6 +45,7 @@ export interface AssistantMcpDiscoveryLoopStateContract {
policy_owner: "assistantMcpDiscoveryRuntimeBridge";
loop_status: AssistantMcpDiscoveryLoopStatus;
selected_chain_id: AssistantMcpDiscoveryChainId;
catalog_chain_template_matches: AssistantMcpDiscoveryPlannerContract["catalog_chain_template_matches"];
pilot_scope: AssistantMcpDiscoveryPilotExecutionContract["pilot_scope"];
asked_domain_family: string | null;
asked_action_family: string | null;
@@ -177,6 +178,7 @@ function buildLoopState(
policy_owner: "assistantMcpDiscoveryRuntimeBridge",
loop_status: loopStatusFor(bridgeStatus),
selected_chain_id: planner.selected_chain_id,
catalog_chain_template_matches: [...planner.catalog_chain_template_matches],
pilot_scope: pilot.pilot_scope,
asked_domain_family: planner.discovery_plan.turn_meaning_ref?.asked_domain_family ?? null,
asked_action_family: planner.discovery_plan.turn_meaning_ref?.asked_action_family ?? null,
@@ -14,6 +14,10 @@ function entryPointContract(overrides: Record<string, unknown> = {}) {
user_facing_response_allowed: true,
business_fact_answer_allowed: true,
requires_user_clarification: false,
planner: {
selected_chain_id: "value_flow_ranking",
catalog_chain_template_matches: ["value_flow_ranking", "value_flow"]
},
answer_draft: {
answer_mode: "confirmed_with_bounded_inference"
}
@@ -37,6 +41,8 @@ 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_catalog_chain_template_matches).toEqual(["value_flow_ranking", "value_flow"]);
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);
@@ -54,6 +60,8 @@ describe("assistant MCP discovery debug attachment", () => {
expect(debug.mcp_discovery_attempted).toBe(false);
expect(debug.mcp_discovery_hot_runtime_wired).toBe(false);
expect(debug.mcp_discovery_bridge_status).toBeNull();
expect(debug.mcp_discovery_selected_chain_id).toBeNull();
expect(debug.mcp_discovery_catalog_chain_template_matches).toEqual([]);
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);
@@ -147,6 +147,7 @@ describe("assistant MCP discovery runtime bridge", () => {
});
expect(result.loop_state.pending_axes).toContain("organization");
expect(result.loop_state.provided_axes).toContain("aggregate_axis");
expect(result.loop_state.catalog_chain_template_matches[0]).toBe("value_flow_ranking");
expect(result.reason_codes).toContain("runtime_bridge_loop_state_awaiting_clarification");
});