ЮИ - Упростить карточки автопрогонов до названия даты id режима и типа

This commit is contained in:
dctouch 2026-04-17 14:09:38 +03:00
parent 9f0f7f3e79
commit 6223062b37
17 changed files with 896 additions and 37 deletions

View File

@ -3,11 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod }; return (mod && mod.__esModule) ? mod : { "default": mod };
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.TRACES_DIR = exports.DATA_DIR = exports.VAT_PAYABLE_19_PREFIXES = exports.VAT_PAYABLE_68_PREFIXES = exports.ASSISTANT_MCP_LIVE_LIMIT = exports.ASSISTANT_MCP_TIMEOUT_MS = exports.ASSISTANT_MCP_CHANNEL = exports.ASSISTANT_MCP_PROXY_URL = exports.FEATURE_ASSISTANT_LIVING_CHAT_ROUTER_V1 = exports.FEATURE_ASSISTANT_ROUTE_EXPECTATION_HARD_GUARD_V1 = exports.FEATURE_ASSISTANT_ROUTE_EXPECTATION_AUDIT_V1 = exports.FEATURE_ASSISTANT_ROUTE_SHADOW_PAYABLES_EXACT_V1 = exports.FEATURE_ASSISTANT_ROUTE_RECEIVABLES_HEURISTIC_V1 = exports.FEATURE_ASSISTANT_ROUTE_PAYABLES_HEURISTIC_V1 = exports.FEATURE_ASSISTANT_ROUTE_RECEIVABLES_CONFIRMED_V1 = exports.FEATURE_ASSISTANT_ROUTE_PAYABLES_CONFIRMED_V1 = exports.FEATURE_ASSISTANT_ROUTE_BALANCE_EXACT_V1 = exports.FEATURE_ASSISTANT_ROUTE_DRILLDOWN_V1 = exports.FEATURE_ASSISTANT_ROUTE_ADDRESS_GENERIC_V1 = exports.FEATURE_ASSISTANT_CAPABILITY_ROUTE_GUARD_V1 = exports.FEATURE_ASSISTANT_ADDRESS_NAVIGATION_STATE_V1 = exports.FEATURE_ASSISTANT_ADDRESS_QUERY_LIVE_V1 = exports.FEATURE_ASSISTANT_ADDRESS_QUERY_LLM_PREDECOMPOSE_V1 = exports.FEATURE_ASSISTANT_ADDRESS_QUERY_V1 = exports.FEATURE_ASSISTANT_MCP_RUNTIME_V1 = exports.FEATURE_ASSISTANT_GRAPH_RUNTIME_V1 = exports.FEATURE_ASSISTANT_LIFECYCLE_ANSWER_V1 = exports.FEATURE_ASSISTANT_LIFECYCLE_RUNTIME_V1 = exports.FEATURE_ASSISTANT_STAGE2_EVAL_V1 = exports.FEATURE_ASSISTANT_PROBLEM_UNIT_CONTINUITY_V1 = exports.FEATURE_ASSISTANT_PROBLEM_CENTRIC_ANSWER_V1 = exports.FEATURE_ASSISTANT_PROBLEM_UNITS_V1 = exports.FEATURE_ASSISTANT_ACCOUNTANT_EVAL_V1 = exports.FEATURE_ASSISTANT_ANSWER_POLICY_V11 = exports.FEATURE_ASSISTANT_ANTI_GENERIC_RANKING_GUARD_V1 = exports.FEATURE_ASSISTANT_MIN_EVIDENCE_GATE_V1 = exports.FEATURE_ASSISTANT_BROAD_GUARD_V1 = exports.FEATURE_ASSISTANT_EVIDENCE_ENRICHMENT_V1 = exports.FEATURE_ASSISTANT_STATE_FOLLOWUP_BINDING_V1 = exports.FEATURE_ASSISTANT_CONTRACTS_V11 = exports.FEATURE_ASSISTANT_INVESTIGATION_STATE_V1 = exports.DEFAULT_PROMPT_VERSION = exports.DEFAULT_MAX_OUTPUT_TOKENS = exports.DEFAULT_TEMPERATURE = exports.DEFAULT_MODEL = exports.DEFAULT_OPENAI_BASE_URL = exports.TIMEZONE = exports.PORT = exports.MODULE_ROOT = exports.BACKEND_ROOT = void 0; exports.DATA_DIR = exports.VAT_PAYABLE_19_PREFIXES = exports.VAT_PAYABLE_68_PREFIXES = exports.ASSISTANT_MCP_LIVE_LIMIT = exports.ASSISTANT_MCP_TIMEOUT_MS = exports.ASSISTANT_MCP_CHANNEL = exports.ASSISTANT_MCP_PROXY_URL = exports.FEATURE_ASSISTANT_LIVING_CHAT_ROUTER_V1 = exports.FEATURE_ASSISTANT_ROUTE_EXPECTATION_HARD_GUARD_V1 = exports.FEATURE_ASSISTANT_ROUTE_EXPECTATION_AUDIT_V1 = exports.FEATURE_ASSISTANT_ROUTE_SHADOW_PAYABLES_EXACT_V1 = exports.FEATURE_ASSISTANT_ROUTE_RECEIVABLES_HEURISTIC_V1 = exports.FEATURE_ASSISTANT_ROUTE_PAYABLES_HEURISTIC_V1 = exports.FEATURE_ASSISTANT_ROUTE_RECEIVABLES_CONFIRMED_V1 = exports.FEATURE_ASSISTANT_ROUTE_PAYABLES_CONFIRMED_V1 = exports.FEATURE_ASSISTANT_ROUTE_BALANCE_EXACT_V1 = exports.FEATURE_ASSISTANT_ROUTE_DRILLDOWN_V1 = exports.FEATURE_ASSISTANT_ROUTE_ADDRESS_GENERIC_V1 = exports.FEATURE_ASSISTANT_CAPABILITY_ROUTE_GUARD_V1 = exports.FEATURE_ASSISTANT_ADDRESS_NAVIGATION_STATE_V1 = exports.FEATURE_ASSISTANT_ADDRESS_QUERY_LIVE_V1 = exports.FEATURE_ASSISTANT_ADDRESS_QUERY_LLM_PREDECOMPOSE_V1 = exports.FEATURE_ASSISTANT_ADDRESS_QUERY_V1 = exports.FEATURE_ASSISTANT_MCP_RUNTIME_V1 = exports.FEATURE_ASSISTANT_GRAPH_RUNTIME_V1 = exports.FEATURE_ASSISTANT_LIFECYCLE_ANSWER_V1 = exports.FEATURE_ASSISTANT_LIFECYCLE_RUNTIME_V1 = exports.FEATURE_ASSISTANT_STAGE2_EVAL_V1 = exports.FEATURE_ASSISTANT_PROBLEM_UNIT_CONTINUITY_V1 = exports.FEATURE_ASSISTANT_PROBLEM_CENTRIC_ANSWER_V1 = exports.FEATURE_ASSISTANT_PROBLEM_UNITS_V1 = exports.FEATURE_ASSISTANT_ACCOUNTANT_EVAL_V1 = exports.FEATURE_ASSISTANT_ANSWER_POLICY_V11 = exports.FEATURE_ASSISTANT_ANTI_GENERIC_RANKING_GUARD_V1 = exports.FEATURE_ASSISTANT_MIN_EVIDENCE_GATE_V1 = exports.FEATURE_ASSISTANT_BROAD_GUARD_V1 = exports.FEATURE_ASSISTANT_EVIDENCE_ENRICHMENT_V1 = exports.FEATURE_ASSISTANT_STATE_FOLLOWUP_BINDING_V1 = exports.FEATURE_ASSISTANT_CONTRACTS_V11 = exports.FEATURE_ASSISTANT_INVESTIGATION_STATE_V1 = exports.DEFAULT_PROMPT_VERSION = exports.DEFAULT_MAX_OUTPUT_TOKENS = exports.DEFAULT_TEMPERATURE = exports.DEFAULT_MODEL = exports.DEFAULT_OPENAI_BASE_URL = exports.TIMEZONE = exports.PORT = exports.PROJECT_ROOT = exports.MODULE_ROOT = exports.BACKEND_ROOT = void 0;
exports.MANUAL_CASE_DECISION_SCHEMA_FILE = exports.ASSISTANT_CAPABILITIES_REGISTRY_FILE = exports.ASSISTANT_CANON_FILE = exports.ARCH_EXPORT_2020_DIR = exports.SCHEMAS_DIR = exports.EVAL_DATASETS_DIR = exports.REPORTS_DIR = exports.PROMPTS_DIR = exports.SHARED_LLM_CONNECTION_FILE = exports.AUTORUN_GENERATOR_HISTORY_FILE = exports.AUTORUN_GENERATOR_DIR = exports.AUTORUN_ANNOTATIONS_FILE = exports.AUTORUN_ANNOTATIONS_DIR = exports.ASSISTANT_ANNOTATIONS_FILE = exports.ASSISTANT_ANNOTATIONS_DIR = exports.ASSISTANT_SESSIONS_DIR = exports.EVAL_CASES_DIR = exports.PRESETS_DIR = void 0; exports.MANUAL_CASE_DECISION_SCHEMA_FILE = exports.ASSISTANT_CAPABILITIES_REGISTRY_FILE = exports.ASSISTANT_CANON_FILE = exports.ARCH_EXPORT_2020_DIR = exports.SCHEMAS_DIR = exports.EVAL_DATASETS_DIR = exports.ARTIFACTS_DIR = exports.REPORTS_DIR = exports.PROMPTS_DIR = exports.SHARED_LLM_CONNECTION_FILE = exports.AUTORUN_GENERATOR_HISTORY_FILE = exports.AUTORUN_GENERATOR_DIR = exports.AUTORUN_ANNOTATIONS_FILE = exports.AUTORUN_ANNOTATIONS_DIR = exports.ASSISTANT_ANNOTATIONS_FILE = exports.ASSISTANT_ANNOTATIONS_DIR = exports.ASSISTANT_SESSIONS_DIR = exports.EVAL_CASES_DIR = exports.PRESETS_DIR = exports.TRACES_DIR = void 0;
const path_1 = __importDefault(require("path")); const path_1 = __importDefault(require("path"));
exports.BACKEND_ROOT = path_1.default.resolve(__dirname, ".."); exports.BACKEND_ROOT = path_1.default.resolve(__dirname, "..");
exports.MODULE_ROOT = path_1.default.resolve(exports.BACKEND_ROOT, ".."); exports.MODULE_ROOT = path_1.default.resolve(exports.BACKEND_ROOT, "..");
exports.PROJECT_ROOT = path_1.default.resolve(exports.MODULE_ROOT, "..");
function toBooleanFlag(value, defaultValue) { function toBooleanFlag(value, defaultValue) {
if (!value || value.trim() === "") { if (!value || value.trim() === "") {
return defaultValue; return defaultValue;
@ -93,6 +94,7 @@ exports.AUTORUN_GENERATOR_HISTORY_FILE = path_1.default.resolve(exports.AUTORUN_
exports.SHARED_LLM_CONNECTION_FILE = path_1.default.resolve(exports.DATA_DIR, "shared_llm_connection.json"); exports.SHARED_LLM_CONNECTION_FILE = path_1.default.resolve(exports.DATA_DIR, "shared_llm_connection.json");
exports.PROMPTS_DIR = path_1.default.resolve(exports.MODULE_ROOT, "prompts"); exports.PROMPTS_DIR = path_1.default.resolve(exports.MODULE_ROOT, "prompts");
exports.REPORTS_DIR = path_1.default.resolve(exports.MODULE_ROOT, "reports"); exports.REPORTS_DIR = path_1.default.resolve(exports.MODULE_ROOT, "reports");
exports.ARTIFACTS_DIR = path_1.default.resolve(exports.PROJECT_ROOT, "artifacts");
exports.EVAL_DATASETS_DIR = path_1.default.resolve(exports.MODULE_ROOT, "eval_cases"); exports.EVAL_DATASETS_DIR = path_1.default.resolve(exports.MODULE_ROOT, "eval_cases");
exports.SCHEMAS_DIR = path_1.default.resolve(exports.BACKEND_ROOT, "src", "schemas"); exports.SCHEMAS_DIR = path_1.default.resolve(exports.BACKEND_ROOT, "src", "schemas");
exports.ARCH_EXPORT_2020_DIR = path_1.default.resolve(exports.MODULE_ROOT, "..", "docs", "ARCH", "2020экспорт"); exports.ARCH_EXPORT_2020_DIR = path_1.default.resolve(exports.MODULE_ROOT, "..", "docs", "ARCH", "2020экспорт");

View File

@ -10,6 +10,7 @@ const path_1 = __importDefault(require("path"));
const express_1 = require("express"); const express_1 = require("express");
const iconv_lite_1 = __importDefault(require("iconv-lite")); const iconv_lite_1 = __importDefault(require("iconv-lite"));
const config_1 = require("../config"); const config_1 = require("../config");
const agentSemanticRunRegistry_1 = require("../services/agentSemanticRunRegistry");
const http_1 = require("../utils/http"); const http_1 = require("../utils/http");
const capabilitiesRegistry_1 = require("../services/capabilitiesRegistry"); const capabilitiesRegistry_1 = require("../services/capabilitiesRegistry");
const openaiResponsesClient_1 = require("../services/openaiResponsesClient"); const openaiResponsesClient_1 = require("../services/openaiResponsesClient");
@ -189,7 +190,14 @@ function readAutoGenHistory() {
? repairAutogenMojibake(String(toRecord(item.context)?.agent_focus)) ? repairAutogenMojibake(String(toRecord(item.context)?.agent_focus))
: null, : null,
architecture_phase: toStringSafe(toRecord(item.context)?.architecture_phase), architecture_phase: toStringSafe(toRecord(item.context)?.architecture_phase),
source_spec_file: toStringSafe(toRecord(item.context)?.source_spec_file) source_spec_file: toStringSafe(toRecord(item.context)?.source_spec_file),
scenario_id: toStringSafe(toRecord(item.context)?.scenario_id),
semantic_tags: Array.isArray(toRecord(item.context)?.semantic_tags)
? Array.from(new Set((toRecord(item.context)?.semantic_tags)
.map((tag) => toStringSafe(tag))
.filter((tag) => Boolean(tag))))
: null,
latest_acceptance: null
} }
: null : null
})) }))
@ -207,6 +215,75 @@ function writeAutoGenHistory(records) {
} }
fs_1.default.writeFileSync(config_1.AUTORUN_GENERATOR_HISTORY_FILE, JSON.stringify(records, null, 2), "utf-8"); fs_1.default.writeFileSync(config_1.AUTORUN_GENERATOR_HISTORY_FILE, JSON.stringify(records, null, 2), "utf-8");
} }
function isAgentSemanticHistoryRecord(record) {
if (record.context?.agent_run === true) {
return true;
}
if (record.context?.saved_case_set_kind === "agent_semantic_scenario") {
return true;
}
return typeof record.title === "string" && record.title.trim().toUpperCase().startsWith("AGENT");
}
function hydrateAutoGenHistoryForApi(records) {
const specSummaryCache = new Map();
const acceptanceCache = new Map();
const readSpecSummary = (sourceSpecFile) => {
const normalizedPath = toStringSafe(sourceSpecFile);
if (!normalizedPath) {
return null;
}
if (specSummaryCache.has(normalizedPath)) {
return specSummaryCache.get(normalizedPath) ?? null;
}
const summary = (0, agentSemanticRunRegistry_1.readAgentSemanticSpecSummaryFromFile)(normalizedPath);
specSummaryCache.set(normalizedPath, summary);
return summary;
};
const readAcceptanceSummary = (scenarioId) => {
const normalizedScenarioId = toStringSafe(scenarioId);
if (!normalizedScenarioId) {
return null;
}
if (acceptanceCache.has(normalizedScenarioId)) {
return acceptanceCache.get(normalizedScenarioId) ?? null;
}
const summary = (0, agentSemanticRunRegistry_1.findLatestAgentSemanticAcceptanceSummary)({
artifactsRootDir: path_1.default.resolve(config_1.ARTIFACTS_DIR, "domain_runs"),
repoRootDir: config_1.PROJECT_ROOT,
scenarioId: normalizedScenarioId
});
acceptanceCache.set(normalizedScenarioId, summary);
return summary;
};
return records.map((record) => {
if (!isAgentSemanticHistoryRecord(record)) {
return record;
}
const specSummary = readSpecSummary(record.context?.source_spec_file);
const scenarioId = toStringSafe(record.context?.scenario_id) ?? specSummary?.scenario_id ?? null;
const semanticTags = Array.isArray(record.context?.semantic_tags) && record.context?.semantic_tags.length > 0
? Array.from(new Set(record.context?.semantic_tags.map((item) => String(item).trim()).filter((item) => item.length > 0)))
: specSummary?.semantic_tags ?? [];
const latestAcceptance = readAcceptanceSummary(scenarioId);
return {
...record,
context: {
...(record.context ?? {
llm_provider: null,
model: null,
assistant_prompt_version: null,
decomposition_prompt_version: null,
prompt_fingerprint: null,
autogen_personality_id: null,
autogen_personality_prompt: null
}),
scenario_id: scenarioId,
semantic_tags: semanticTags,
latest_acceptance: latestAcceptance
}
};
});
}
function readEvalDatasetCases(filePath) { function readEvalDatasetCases(filePath) {
try { try {
const parsed = JSON.parse(fs_1.default.readFileSync(filePath, "utf-8")); const parsed = JSON.parse(fs_1.default.readFileSync(filePath, "utf-8"));
@ -2015,9 +2092,9 @@ function buildAutoRunsRouter(services, openaiClient = new openaiResponsesClient_
const rawMode = toStringSafe(req.query.mode); const rawMode = toStringSafe(req.query.mode);
const includeAllModes = !rawMode || !isAutoGenMode(rawMode); const includeAllModes = !rawMode || !isAutoGenMode(rawMode);
const modeFilter = rawMode ?? "codex_creative"; const modeFilter = rawMode ?? "codex_creative";
const items = readAutoGenHistory() const items = hydrateAutoGenHistoryForApi(readAutoGenHistory()
.filter((item) => (includeAllModes ? true : item.mode === modeFilter)) .filter((item) => (includeAllModes ? true : item.mode === modeFilter))
.slice(0, limit); .slice(0, limit));
(0, http_1.ok)(res, { (0, http_1.ok)(res, {
ok: true, ok: true,
generated_at: new Date().toISOString(), generated_at: new Date().toISOString(),

View File

@ -0,0 +1,166 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.readAgentSemanticSpecSummaryFromFile = readAgentSemanticSpecSummaryFromFile;
exports.findLatestAgentSemanticAcceptanceSummary = findLatestAgentSemanticAcceptanceSummary;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
function toRecord(value) {
if (!value || typeof value !== "object" || Array.isArray(value)) {
return null;
}
return value;
}
function toStringSafe(value) {
if (typeof value !== "string") {
return null;
}
const trimmed = value.trim();
return trimmed.length > 0 ? trimmed : null;
}
function toBooleanSafe(value) {
if (typeof value === "boolean") {
return value;
}
if (typeof value === "string") {
const lowered = value.trim().toLowerCase();
if (["1", "true", "yes", "on"].includes(lowered))
return true;
if (["0", "false", "no", "off"].includes(lowered))
return false;
}
return null;
}
function toNumberSafe(value) {
if (typeof value === "number" && Number.isFinite(value)) {
return value;
}
if (typeof value === "string" && value.trim().length > 0) {
const parsed = Number(value);
return Number.isFinite(parsed) ? parsed : null;
}
return null;
}
function readJsonFile(filePath) {
return JSON.parse(fs_1.default.readFileSync(filePath, "utf-8"));
}
function normalizeSemanticTags(rawSteps) {
if (!Array.isArray(rawSteps)) {
return [];
}
const tags = new Set();
for (const rawStep of rawSteps) {
const step = toRecord(rawStep);
if (!step || !Array.isArray(step.semantic_tags)) {
continue;
}
for (const rawTag of step.semantic_tags) {
const tag = toStringSafe(rawTag);
if (tag) {
tags.add(tag);
}
}
}
return Array.from(tags).sort((left, right) => left.localeCompare(right));
}
function readAgentSemanticSpecSummaryFromFile(sourceSpecFile) {
const normalizedPath = toStringSafe(sourceSpecFile);
if (!normalizedPath || !fs_1.default.existsSync(normalizedPath)) {
return null;
}
try {
const parsed = readJsonFile(normalizedPath);
const record = toRecord(parsed);
if (!record) {
return null;
}
const scenarioId = toStringSafe(record.scenario_id);
if (!scenarioId) {
return null;
}
const semanticTags = normalizeSemanticTags(record.steps);
return {
scenario_id: scenarioId,
domain: toStringSafe(record.domain),
title: toStringSafe(record.title),
semantic_tags: semanticTags,
steps_total: Array.isArray(record.steps) ? record.steps.length : 0
};
}
catch {
return null;
}
}
function buildInvariantSummary(rawValue) {
const record = toRecord(rawValue);
return {
direct_answer_ok: toBooleanSafe(record?.direct_answer_ok),
temporal_honesty_ok: toBooleanSafe(record?.temporal_honesty_ok),
selected_object_continuity_ok: toBooleanSafe(record?.selected_object_continuity_ok),
truth_gate_ok: toBooleanSafe(record?.truth_gate_ok),
human_answer_quality_ok: toBooleanSafe(record?.human_answer_quality_ok),
meta_context_integrity_ok: toBooleanSafe(record?.meta_context_integrity_ok)
};
}
function findLatestAgentSemanticAcceptanceSummary(input) {
const scenarioId = toStringSafe(input.scenarioId);
if (!scenarioId) {
return null;
}
if (!fs_1.default.existsSync(input.artifactsRootDir)) {
return null;
}
const candidates = fs_1.default
.readdirSync(input.artifactsRootDir, { withFileTypes: true })
.filter((entry) => entry.isDirectory() && entry.name.startsWith(`${scenarioId}_`))
.map((entry) => {
const outputDir = path_1.default.resolve(input.artifactsRootDir, entry.name);
const packStatePath = path_1.default.resolve(outputDir, "pack_state.json");
if (!fs_1.default.existsSync(packStatePath)) {
return null;
}
const stats = fs_1.default.statSync(packStatePath);
return {
outputDir,
packStatePath,
mtimeMs: stats.mtimeMs
};
})
.filter((item) => item !== null)
.sort((left, right) => right.mtimeMs - left.mtimeMs);
const latest = candidates[0];
if (!latest) {
return null;
}
try {
const parsed = readJsonFile(latest.packStatePath);
const packState = toRecord(parsed);
if (!packState) {
return null;
}
return {
scenario_id: scenarioId,
output_dir: latest.outputDir,
relative_output_dir: path_1.default.relative(input.repoRootDir, latest.outputDir).replace(/\\/g, "/"),
final_status: toStringSafe(packState.final_status),
final_status_reason: toStringSafe(packState.final_status_reason),
review_overall_status: toStringSafe(packState.review_overall_status),
acceptance_gate_passed: toBooleanSafe(packState.acceptance_gate_passed),
critical_path_green: toBooleanSafe(packState.critical_path_green),
unresolved_p0_count: toNumberSafe(packState.unresolved_p0_count),
unresolved_p1_count: toNumberSafe(packState.unresolved_p1_count),
unresolved_p2_count: toNumberSafe(packState.unresolved_p2_count),
steps_total: toNumberSafe(packState.steps_total),
steps_passed: toNumberSafe(packState.steps_passed),
steps_with_warning: toNumberSafe(packState.steps_with_warning),
steps_failed: toNumberSafe(packState.steps_failed),
updated_at: toStringSafe(packState.updated_at),
invariants: buildInvariantSummary(packState.invariants)
};
}
catch {
return null;
}
}

View File

@ -2,6 +2,7 @@ import path from "path";
export const BACKEND_ROOT = path.resolve(__dirname, ".."); export const BACKEND_ROOT = path.resolve(__dirname, "..");
export const MODULE_ROOT = path.resolve(BACKEND_ROOT, ".."); export const MODULE_ROOT = path.resolve(BACKEND_ROOT, "..");
export const PROJECT_ROOT = path.resolve(MODULE_ROOT, "..");
function toBooleanFlag(value: string | undefined, defaultValue: boolean): boolean { function toBooleanFlag(value: string | undefined, defaultValue: boolean): boolean {
if (!value || value.trim() === "") { if (!value || value.trim() === "") {
@ -192,6 +193,7 @@ export const SHARED_LLM_CONNECTION_FILE = path.resolve(DATA_DIR, "shared_llm_con
export const PROMPTS_DIR = path.resolve(MODULE_ROOT, "prompts"); export const PROMPTS_DIR = path.resolve(MODULE_ROOT, "prompts");
export const REPORTS_DIR = path.resolve(MODULE_ROOT, "reports"); export const REPORTS_DIR = path.resolve(MODULE_ROOT, "reports");
export const ARTIFACTS_DIR = path.resolve(PROJECT_ROOT, "artifacts");
export const EVAL_DATASETS_DIR = path.resolve(MODULE_ROOT, "eval_cases"); export const EVAL_DATASETS_DIR = path.resolve(MODULE_ROOT, "eval_cases");
export const SCHEMAS_DIR = path.resolve(BACKEND_ROOT, "src", "schemas"); export const SCHEMAS_DIR = path.resolve(BACKEND_ROOT, "src", "schemas");
export const ARCH_EXPORT_2020_DIR = path.resolve(MODULE_ROOT, "..", "docs", "ARCH", "2020экспорт"); export const ARCH_EXPORT_2020_DIR = path.resolve(MODULE_ROOT, "..", "docs", "ARCH", "2020экспорт");

View File

@ -3,15 +3,21 @@ import path from "path";
import { Router } from "express"; import { Router } from "express";
import iconv from "iconv-lite"; import iconv from "iconv-lite";
import { import {
ARTIFACTS_DIR,
ASSISTANT_SESSIONS_DIR, ASSISTANT_SESSIONS_DIR,
AUTORUN_ANNOTATIONS_FILE, AUTORUN_ANNOTATIONS_FILE,
AUTORUN_GENERATOR_HISTORY_FILE, AUTORUN_GENERATOR_HISTORY_FILE,
EVAL_CASES_DIR, EVAL_CASES_DIR,
EVAL_DATASETS_DIR, EVAL_DATASETS_DIR,
MANUAL_CASE_DECISION_SCHEMA_FILE, MANUAL_CASE_DECISION_SCHEMA_FILE,
PROJECT_ROOT,
REPORTS_DIR REPORTS_DIR
} from "../config"; } from "../config";
import type { AppServices } from "../serverContext"; import type { AppServices } from "../serverContext";
import {
findLatestAgentSemanticAcceptanceSummary,
readAgentSemanticSpecSummaryFromFile
} from "../services/agentSemanticRunRegistry";
import { ApiError, ok } from "../utils/http"; import { ApiError, ok } from "../utils/http";
import { loadCapabilitiesRegistry, resolveNearestCapabilityGroup, type CapabilityGroup } from "../services/capabilitiesRegistry"; import { loadCapabilitiesRegistry, resolveNearestCapabilityGroup, type CapabilityGroup } from "../services/capabilitiesRegistry";
import { OpenAIResponsesClient } from "../services/openaiResponsesClient"; import { OpenAIResponsesClient } from "../services/openaiResponsesClient";
@ -197,6 +203,34 @@ interface AutoGenHistoryRecord {
agent_focus?: string | null; agent_focus?: string | null;
architecture_phase?: string | null; architecture_phase?: string | null;
source_spec_file?: string | null; source_spec_file?: string | null;
scenario_id?: string | null;
semantic_tags?: string[] | null;
latest_acceptance?: {
scenario_id: string;
output_dir: string;
relative_output_dir: string;
final_status: string | null;
final_status_reason: string | null;
review_overall_status: string | null;
acceptance_gate_passed: boolean | null;
critical_path_green: boolean | null;
unresolved_p0_count: number | null;
unresolved_p1_count: number | null;
unresolved_p2_count: number | null;
steps_total: number | null;
steps_passed: number | null;
steps_with_warning: number | null;
steps_failed: number | null;
updated_at: string | null;
invariants: {
direct_answer_ok: boolean | null;
temporal_honesty_ok: boolean | null;
selected_object_continuity_ok: boolean | null;
truth_gate_ok: boolean | null;
human_answer_quality_ok: boolean | null;
meta_context_integrity_ok: boolean | null;
};
} | null;
} | null; } | null;
} }
@ -375,7 +409,18 @@ function readAutoGenHistory(): AutoGenHistoryRecord[] {
? repairAutogenMojibake(String(toRecord(item.context)?.agent_focus)) ? repairAutogenMojibake(String(toRecord(item.context)?.agent_focus))
: null, : null,
architecture_phase: toStringSafe(toRecord(item.context)?.architecture_phase), architecture_phase: toStringSafe(toRecord(item.context)?.architecture_phase),
source_spec_file: toStringSafe(toRecord(item.context)?.source_spec_file) source_spec_file: toStringSafe(toRecord(item.context)?.source_spec_file),
scenario_id: toStringSafe(toRecord(item.context)?.scenario_id),
semantic_tags: Array.isArray(toRecord(item.context)?.semantic_tags)
? Array.from(
new Set(
(toRecord(item.context)?.semantic_tags as unknown[])
.map((tag) => toStringSafe(tag))
.filter((tag): tag is string => Boolean(tag))
)
)
: null,
latest_acceptance: null
} }
: null : null
})) }))
@ -394,6 +439,81 @@ function writeAutoGenHistory(records: AutoGenHistoryRecord[]): void {
fs.writeFileSync(AUTORUN_GENERATOR_HISTORY_FILE, JSON.stringify(records, null, 2), "utf-8"); fs.writeFileSync(AUTORUN_GENERATOR_HISTORY_FILE, JSON.stringify(records, null, 2), "utf-8");
} }
function isAgentSemanticHistoryRecord(record: AutoGenHistoryRecord): boolean {
if (record.context?.agent_run === true) {
return true;
}
if (record.context?.saved_case_set_kind === "agent_semantic_scenario") {
return true;
}
return typeof record.title === "string" && record.title.trim().toUpperCase().startsWith("AGENT");
}
function hydrateAutoGenHistoryForApi(records: AutoGenHistoryRecord[]): AutoGenHistoryRecord[] {
const specSummaryCache = new Map<string, ReturnType<typeof readAgentSemanticSpecSummaryFromFile>>();
const acceptanceCache = new Map<string, ReturnType<typeof findLatestAgentSemanticAcceptanceSummary>>();
const readSpecSummary = (sourceSpecFile: string | null | undefined) => {
const normalizedPath = toStringSafe(sourceSpecFile);
if (!normalizedPath) {
return null;
}
if (specSummaryCache.has(normalizedPath)) {
return specSummaryCache.get(normalizedPath) ?? null;
}
const summary = readAgentSemanticSpecSummaryFromFile(normalizedPath);
specSummaryCache.set(normalizedPath, summary);
return summary;
};
const readAcceptanceSummary = (scenarioId: string | null | undefined) => {
const normalizedScenarioId = toStringSafe(scenarioId);
if (!normalizedScenarioId) {
return null;
}
if (acceptanceCache.has(normalizedScenarioId)) {
return acceptanceCache.get(normalizedScenarioId) ?? null;
}
const summary = findLatestAgentSemanticAcceptanceSummary({
artifactsRootDir: path.resolve(ARTIFACTS_DIR, "domain_runs"),
repoRootDir: PROJECT_ROOT,
scenarioId: normalizedScenarioId
});
acceptanceCache.set(normalizedScenarioId, summary);
return summary;
};
return records.map((record) => {
if (!isAgentSemanticHistoryRecord(record)) {
return record;
}
const specSummary = readSpecSummary(record.context?.source_spec_file);
const scenarioId = toStringSafe(record.context?.scenario_id) ?? specSummary?.scenario_id ?? null;
const semanticTags =
Array.isArray(record.context?.semantic_tags) && record.context?.semantic_tags.length > 0
? Array.from(new Set(record.context?.semantic_tags.map((item) => String(item).trim()).filter((item) => item.length > 0)))
: specSummary?.semantic_tags ?? [];
const latestAcceptance = readAcceptanceSummary(scenarioId);
return {
...record,
context: {
...(record.context ?? {
llm_provider: null,
model: null,
assistant_prompt_version: null,
decomposition_prompt_version: null,
prompt_fingerprint: null,
autogen_personality_id: null,
autogen_personality_prompt: null
}),
scenario_id: scenarioId,
semantic_tags: semanticTags,
latest_acceptance: latestAcceptance
}
};
});
}
function readEvalDatasetCases(filePath: string): Array<Record<string, unknown>> { function readEvalDatasetCases(filePath: string): Array<Record<string, unknown>> {
try { try {
const parsed = JSON.parse(fs.readFileSync(filePath, "utf-8")) as unknown; const parsed = JSON.parse(fs.readFileSync(filePath, "utf-8")) as unknown;
@ -2416,9 +2536,11 @@ export function buildAutoRunsRouter(services: AppServices, openaiClient = new Op
const rawMode = toStringSafe((req.query as Record<string, unknown>).mode); const rawMode = toStringSafe((req.query as Record<string, unknown>).mode);
const includeAllModes = !rawMode || !isAutoGenMode(rawMode); const includeAllModes = !rawMode || !isAutoGenMode(rawMode);
const modeFilter = (rawMode as AutoGenMode | null) ?? "codex_creative"; const modeFilter = (rawMode as AutoGenMode | null) ?? "codex_creative";
const items = readAutoGenHistory() const items = hydrateAutoGenHistoryForApi(
readAutoGenHistory()
.filter((item) => (includeAllModes ? true : item.mode === modeFilter)) .filter((item) => (includeAllModes ? true : item.mode === modeFilter))
.slice(0, limit); .slice(0, limit)
);
ok(res, { ok(res, {
ok: true, ok: true,

View File

@ -0,0 +1,208 @@
import fs from "fs";
import path from "path";
interface AgentSemanticSpecSummary {
scenario_id: string;
domain: string | null;
title: string | null;
semantic_tags: string[];
steps_total: number;
}
interface AgentSemanticInvariantSummary {
direct_answer_ok: boolean | null;
temporal_honesty_ok: boolean | null;
selected_object_continuity_ok: boolean | null;
truth_gate_ok: boolean | null;
human_answer_quality_ok: boolean | null;
meta_context_integrity_ok: boolean | null;
}
export interface AgentSemanticAcceptanceSummary {
scenario_id: string;
output_dir: string;
relative_output_dir: string;
final_status: string | null;
final_status_reason: string | null;
review_overall_status: string | null;
acceptance_gate_passed: boolean | null;
critical_path_green: boolean | null;
unresolved_p0_count: number | null;
unresolved_p1_count: number | null;
unresolved_p2_count: number | null;
steps_total: number | null;
steps_passed: number | null;
steps_with_warning: number | null;
steps_failed: number | null;
updated_at: string | null;
invariants: AgentSemanticInvariantSummary;
}
function toRecord(value: unknown): Record<string, unknown> | null {
if (!value || typeof value !== "object" || Array.isArray(value)) {
return null;
}
return value as Record<string, unknown>;
}
function toStringSafe(value: unknown): string | null {
if (typeof value !== "string") {
return null;
}
const trimmed = value.trim();
return trimmed.length > 0 ? trimmed : null;
}
function toBooleanSafe(value: unknown): boolean | null {
if (typeof value === "boolean") {
return value;
}
if (typeof value === "string") {
const lowered = value.trim().toLowerCase();
if (["1", "true", "yes", "on"].includes(lowered)) return true;
if (["0", "false", "no", "off"].includes(lowered)) return false;
}
return null;
}
function toNumberSafe(value: unknown): number | null {
if (typeof value === "number" && Number.isFinite(value)) {
return value;
}
if (typeof value === "string" && value.trim().length > 0) {
const parsed = Number(value);
return Number.isFinite(parsed) ? parsed : null;
}
return null;
}
function readJsonFile(filePath: string): unknown {
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
}
function normalizeSemanticTags(rawSteps: unknown): string[] {
if (!Array.isArray(rawSteps)) {
return [];
}
const tags = new Set<string>();
for (const rawStep of rawSteps) {
const step = toRecord(rawStep);
if (!step || !Array.isArray(step.semantic_tags)) {
continue;
}
for (const rawTag of step.semantic_tags) {
const tag = toStringSafe(rawTag);
if (tag) {
tags.add(tag);
}
}
}
return Array.from(tags).sort((left, right) => left.localeCompare(right));
}
export function readAgentSemanticSpecSummaryFromFile(sourceSpecFile: string | null | undefined): AgentSemanticSpecSummary | null {
const normalizedPath = toStringSafe(sourceSpecFile);
if (!normalizedPath || !fs.existsSync(normalizedPath)) {
return null;
}
try {
const parsed = readJsonFile(normalizedPath);
const record = toRecord(parsed);
if (!record) {
return null;
}
const scenarioId = toStringSafe(record.scenario_id);
if (!scenarioId) {
return null;
}
const semanticTags = normalizeSemanticTags(record.steps);
return {
scenario_id: scenarioId,
domain: toStringSafe(record.domain),
title: toStringSafe(record.title),
semantic_tags: semanticTags,
steps_total: Array.isArray(record.steps) ? record.steps.length : 0
};
} catch {
return null;
}
}
function buildInvariantSummary(rawValue: unknown): AgentSemanticInvariantSummary {
const record = toRecord(rawValue);
return {
direct_answer_ok: toBooleanSafe(record?.direct_answer_ok),
temporal_honesty_ok: toBooleanSafe(record?.temporal_honesty_ok),
selected_object_continuity_ok: toBooleanSafe(record?.selected_object_continuity_ok),
truth_gate_ok: toBooleanSafe(record?.truth_gate_ok),
human_answer_quality_ok: toBooleanSafe(record?.human_answer_quality_ok),
meta_context_integrity_ok: toBooleanSafe(record?.meta_context_integrity_ok)
};
}
export function findLatestAgentSemanticAcceptanceSummary(input: {
artifactsRootDir: string;
repoRootDir: string;
scenarioId: string | null | undefined;
}): AgentSemanticAcceptanceSummary | null {
const scenarioId = toStringSafe(input.scenarioId);
if (!scenarioId) {
return null;
}
if (!fs.existsSync(input.artifactsRootDir)) {
return null;
}
const candidates = fs
.readdirSync(input.artifactsRootDir, { withFileTypes: true })
.filter((entry) => entry.isDirectory() && entry.name.startsWith(`${scenarioId}_`))
.map((entry) => {
const outputDir = path.resolve(input.artifactsRootDir, entry.name);
const packStatePath = path.resolve(outputDir, "pack_state.json");
if (!fs.existsSync(packStatePath)) {
return null;
}
const stats = fs.statSync(packStatePath);
return {
outputDir,
packStatePath,
mtimeMs: stats.mtimeMs
};
})
.filter((item): item is { outputDir: string; packStatePath: string; mtimeMs: number } => item !== null)
.sort((left, right) => right.mtimeMs - left.mtimeMs);
const latest = candidates[0];
if (!latest) {
return null;
}
try {
const parsed = readJsonFile(latest.packStatePath);
const packState = toRecord(parsed);
if (!packState) {
return null;
}
return {
scenario_id: scenarioId,
output_dir: latest.outputDir,
relative_output_dir: path.relative(input.repoRootDir, latest.outputDir).replace(/\\/g, "/"),
final_status: toStringSafe(packState.final_status),
final_status_reason: toStringSafe(packState.final_status_reason),
review_overall_status: toStringSafe(packState.review_overall_status),
acceptance_gate_passed: toBooleanSafe(packState.acceptance_gate_passed),
critical_path_green: toBooleanSafe(packState.critical_path_green),
unresolved_p0_count: toNumberSafe(packState.unresolved_p0_count),
unresolved_p1_count: toNumberSafe(packState.unresolved_p1_count),
unresolved_p2_count: toNumberSafe(packState.unresolved_p2_count),
steps_total: toNumberSafe(packState.steps_total),
steps_passed: toNumberSafe(packState.steps_passed),
steps_with_warning: toNumberSafe(packState.steps_with_warning),
steps_failed: toNumberSafe(packState.steps_failed),
updated_at: toStringSafe(packState.updated_at),
invariants: buildInvariantSummary(packState.invariants)
};
} catch {
return null;
}
}

View File

@ -0,0 +1,128 @@
import fs from "fs";
import os from "os";
import path from "path";
import { afterEach, describe, expect, it } from "vitest";
import {
findLatestAgentSemanticAcceptanceSummary,
readAgentSemanticSpecSummaryFromFile
} from "../src/services/agentSemanticRunRegistry";
const tempRoots: string[] = [];
function makeTempRoot(): string {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "agent-semantic-registry-"));
tempRoots.push(root);
return root;
}
function writeJson(filePath: string, payload: unknown): void {
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf-8");
}
afterEach(() => {
while (tempRoots.length > 0) {
const root = tempRoots.pop();
if (root && fs.existsSync(root)) {
fs.rmSync(root, { recursive: true, force: true });
}
}
});
describe("agentSemanticRunRegistry", () => {
it("reads scenario id and deduplicated semantic tags from the source spec", () => {
const root = makeTempRoot();
const specPath = path.resolve(root, "docs", "orchestration", "sample_agent_spec.json");
writeJson(specPath, {
scenario_id: "address_truth_harness_demo",
domain: "address_demo",
title: "Demo AGENT spec",
steps: [
{ step_id: "step_01", question: "привет", semantic_tags: ["meta_smalltalk", "meta_scope"] },
{ step_id: "step_02", question: "остатки", semantic_tags: ["inventory_root", "meta_scope"] }
]
});
const summary = readAgentSemanticSpecSummaryFromFile(specPath);
expect(summary).toEqual({
scenario_id: "address_truth_harness_demo",
domain: "address_demo",
title: "Demo AGENT spec",
semantic_tags: ["inventory_root", "meta_scope", "meta_smalltalk"],
steps_total: 2
});
});
it("picks the newest pack_state for the scenario and exposes acceptance summary", () => {
const root = makeTempRoot();
const artifactsRoot = path.resolve(root, "artifacts", "domain_runs");
const olderDir = path.resolve(artifactsRoot, "address_truth_harness_demo_live_20260417");
const olderPackStatePath = path.resolve(olderDir, "pack_state.json");
writeJson(olderPackStatePath, {
final_status: "partial",
final_status_reason: "review_failures_remaining",
review_overall_status: "fail",
acceptance_gate_passed: false,
critical_path_green: false,
unresolved_p0_count: 1,
unresolved_p1_count: 0,
unresolved_p2_count: 0,
steps_total: 4,
steps_passed: 2,
steps_with_warning: 1,
steps_failed: 1,
updated_at: "2026-04-17T09:00:00+00:00",
invariants: {
direct_answer_ok: false,
temporal_honesty_ok: true,
selected_object_continuity_ok: false,
truth_gate_ok: true,
human_answer_quality_ok: true,
meta_context_integrity_ok: true
}
});
fs.utimesSync(olderPackStatePath, new Date("2026-04-17T09:00:00.000Z"), new Date("2026-04-17T09:00:00.000Z"));
const newerDir = path.resolve(artifactsRoot, "address_truth_harness_demo_live_20260417_rerun2");
const newerPackStatePath = path.resolve(newerDir, "pack_state.json");
writeJson(newerPackStatePath, {
final_status: "accepted",
final_status_reason: "scenario_acceptance_gate_passed",
review_overall_status: "pass",
acceptance_gate_passed: true,
critical_path_green: true,
unresolved_p0_count: 0,
unresolved_p1_count: 0,
unresolved_p2_count: 0,
steps_total: 4,
steps_passed: 4,
steps_with_warning: 0,
steps_failed: 0,
updated_at: "2026-04-17T09:10:00+00:00",
invariants: {
direct_answer_ok: true,
temporal_honesty_ok: true,
selected_object_continuity_ok: true,
truth_gate_ok: true,
human_answer_quality_ok: true,
meta_context_integrity_ok: true
}
});
fs.utimesSync(newerPackStatePath, new Date("2026-04-17T09:10:00.000Z"), new Date("2026-04-17T09:10:00.000Z"));
const summary = findLatestAgentSemanticAcceptanceSummary({
artifactsRootDir: artifactsRoot,
repoRootDir: root,
scenarioId: "address_truth_harness_demo"
});
expect(summary).not.toBeNull();
expect(summary?.final_status).toBe("accepted");
expect(summary?.relative_output_dir).toBe("artifacts/domain_runs/address_truth_harness_demo_live_20260417_rerun2");
expect(summary?.steps_passed).toBe(4);
expect(summary?.unresolved_p0_count).toBe(0);
expect(summary?.invariants.meta_context_integrity_ok).toBe(true);
});
});

View File

@ -0,0 +1,63 @@
{
"suite_id": "assistant_saved_session_runtime_job-WKk8Q3UqdX",
"suite_version": "0.1.0",
"schema_version": "assistant_saved_session_runtime_v0_1",
"title": "AGENT | Phase 7 mixed replay for documents, selected-object continuity, meta context, and cross-domain pivots",
"scenario_count": 1,
"case_ids": [
"SAVED-001"
],
"cases": [
{
"case_id": "SAVED-001",
"scenario_tag": "saved_user_sessions_runtime",
"title": "AGENT | Phase 7 mixed replay for documents, selected-object continuity, meta context, and cross-domain pivots",
"question_type": "followup",
"broadness_level": "medium",
"turns": [
{
"user_message": "привет, как дела?"
},
{
"user_message": "по какой компании мы сейчас работаем?"
},
{
"user_message": "покажи все документы по чепурнову"
},
{
"user_message": "что нам отгружал чепурнов, какой товар или услугу?"
},
{
"user_message": "какие остатки на складе на март 2021"
},
{
"user_message": "По выбранному объекту \"Столешница 600*3050*26 альмандин\": кто нам это поставил?"
},
{
"user_message": "что ты умеешь?"
},
{
"user_message": "По выбранному объекту \"Столешница 600*3050*26 альмандин\": покажи документы по этой позиции"
},
{
"user_message": "а ты помнишь, что мы по этой позиции уже выяснили?"
},
{
"user_message": "покажи еще раз остатки на эту же дату"
},
{
"user_message": "кто нам должен на март 2020"
},
{
"user_message": "остатки по складу на эту же дату"
},
{
"user_message": "а исторические остатки тоже можешь?"
},
{
"user_message": "хвосты покажи по счету 60 на август 2022"
}
]
}
]
}

View File

@ -1,6 +1,6 @@
{ {
"schema_version": "shared_llm_connection_v1", "schema_version": "shared_llm_connection_v1",
"updated_at": "2026-04-16T17:54:57.636Z", "updated_at": "2026-04-17T10:55:35.860Z",
"connection": { "connection": {
"llmProvider": "local", "llmProvider": "local",
"model": "unsloth/qwen3-30b-a3b-instruct-2507", "model": "unsloth/qwen3-30b-a3b-instruct-2507",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -4,8 +4,8 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>NDC AI Normalizer Playground</title> <title>NDC AI Normalizer Playground</title>
<script type="module" crossorigin src="/assets/index-BIzNO_Mb.js"></script> <script type="module" crossorigin src="/assets/index-C8U6PD78.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DkWsdP2H.css"> <link rel="stylesheet" crossorigin href="/assets/index-CfrZGsZo.css">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View File

@ -1534,6 +1534,7 @@ export function AutoRunsHistoryPanel({
selectedAutogenGeneration, selectedAutogenGeneration,
stopAsyncJobPolling stopAsyncJobPolling
]); ]);
const openCommentModal = useCallback((message: AutoRunDialogMessage) => { const openCommentModal = useCallback((message: AutoRunDialogMessage) => {
if (message.role !== "assistant") return; if (message.role !== "assistant") return;
const resolvedCaseId = message.case_id ?? selectedCaseId; const resolvedCaseId = message.case_id ?? selectedCaseId;
@ -2507,23 +2508,11 @@ export function AutoRunsHistoryPanel({
</span> </span>
</div> </div>
<div className="autoruns-run-meta"> <div className="autoruns-run-meta">
режим={formatAutoGenModeLabel(item.mode)} | count={item.count} режим={formatAutoGenModeLabel(item.mode)}
</div> </div>
{isAgentSemanticGeneration(item) ? (
<div className="autoruns-run-meta"> <div className="autoruns-run-meta">
тип=АГЕНТНЫЙ ПРОГОН тип={isAgentSemanticGeneration(item) ? "АГЕНТНЫЙ ПРОГОН" : "АВТОПРОГОН"}
{item.context?.architecture_phase ? ` | этап=${item.context.architecture_phase}` : ""}
{item.context?.agent_focus ? ` | фокус=${item.context.agent_focus}` : ""}
</div> </div>
) : null}
{item.domain || item.generated_by ? (
<div className="autoruns-run-meta">
{item.domain ? `домен=${item.domain}` : "домен=общий"}
{item.generated_by ? ` | автор=${item.generated_by}` : ""}
</div>
) : null}
{item.saved_case_set_file ? <div className="autoruns-run-meta">кейс-сет={item.saved_case_set_file}</div> : null}
{(item.questions ?? []).length > 0 ? <p>{item.questions[0]}</p> : null}
</article> </article>
))} ))}
</div> </div>

View File

@ -338,6 +338,34 @@ export interface AutoGenHistoryRecord {
agent_focus?: string | null; agent_focus?: string | null;
architecture_phase?: string | null; architecture_phase?: string | null;
source_spec_file?: string | null; source_spec_file?: string | null;
scenario_id?: string | null;
semantic_tags?: string[] | null;
latest_acceptance?: {
scenario_id: string;
output_dir: string;
relative_output_dir: string;
final_status: string | null;
final_status_reason: string | null;
review_overall_status: string | null;
acceptance_gate_passed: boolean | null;
critical_path_green: boolean | null;
unresolved_p0_count: number | null;
unresolved_p1_count: number | null;
unresolved_p2_count: number | null;
steps_total: number | null;
steps_passed: number | null;
steps_with_warning: number | null;
steps_failed: number | null;
updated_at: string | null;
invariants: {
direct_answer_ok: boolean | null;
temporal_honesty_ok: boolean | null;
selected_object_continuity_ok: boolean | null;
truth_gate_ok: boolean | null;
human_answer_quality_ok: boolean | null;
meta_context_integrity_ok: boolean | null;
};
} | null;
} | null; } | null;
} }

View File

@ -13,6 +13,8 @@
--rgb-active-text: 18, 18, 18; --rgb-active-text: 18, 18, 18;
--rgb-text-main: 240, 240, 240; --rgb-text-main: 240, 240, 240;
--rgb-text-muted: 166, 166, 170; --rgb-text-muted: 166, 166, 170;
--rgb-emerald: 126, 211, 154;
--rgb-warning: 255, 196, 116;
--rgb-danger: 255, 126, 126; --rgb-danger: 255, 126, 126;
--rgb-scrollbar-track: 31, 31, 36; --rgb-scrollbar-track: 31, 31, 36;
--rgb-scrollbar-thumb: 74, 74, 82; --rgb-scrollbar-thumb: 74, 74, 82;
@ -1802,6 +1804,55 @@ button:disabled {
box-shadow: none; box-shadow: none;
} }
.autoruns-agent-acceptance {
display: grid;
gap: 6px;
margin-top: 6px;
}
.autoruns-agent-pill-row {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.autoruns-agent-pill {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 24px;
padding: 0 8px;
border-radius: 999px;
background: rgb(var(--rgb-surface-focus));
color: rgb(var(--rgb-text-main));
font-size: 0.73rem;
line-height: 1;
border: 1px solid rgba(var(--rgb-text-main), 0.1);
}
.autoruns-agent-pill.neutral {
color: var(--text-muted);
}
.autoruns-agent-pill.status-accepted {
color: rgb(var(--rgb-emerald));
border-color: rgba(var(--rgb-emerald), 0.32);
}
.autoruns-agent-pill.status-partial {
color: rgb(var(--rgb-warning));
border-color: rgba(var(--rgb-warning), 0.32);
}
.autoruns-agent-pill.status-blocked {
color: rgb(var(--rgb-danger));
border-color: rgba(var(--rgb-danger), 0.32);
}
.autoruns-agent-pill.status-unknown {
color: var(--text-muted);
}
@media (max-width: 1200px) { @media (max-width: 1200px) {
:root { :root {
--mode-column-width: 400px; --mode-column-width: 400px;

View File

@ -66,6 +66,24 @@ def normalize_questions(raw_questions: list[Any]) -> list[str]:
return result return result
def extract_semantic_tags(spec: dict[str, Any]) -> list[str]:
steps = spec.get("steps")
if not isinstance(steps, list):
return []
tags: set[str] = set()
for step in steps:
if not isinstance(step, dict):
continue
raw_tags = step.get("semantic_tags")
if not isinstance(raw_tags, list):
continue
for raw_tag in raw_tags:
tag = str(raw_tag or "").strip()
if tag:
tags.add(tag)
return sorted(tags)
def extract_questions_from_spec(spec: dict[str, Any]) -> list[str]: def extract_questions_from_spec(spec: dict[str, Any]) -> list[str]:
if isinstance(spec.get("questions"), list): if isinstance(spec.get("questions"), list):
return normalize_questions(list(spec["questions"])) return normalize_questions(list(spec["questions"]))
@ -183,6 +201,8 @@ def build_history_record(
"agent_focus": metadata.get("agent_focus"), "agent_focus": metadata.get("agent_focus"),
"architecture_phase": metadata.get("architecture_phase"), "architecture_phase": metadata.get("architecture_phase"),
"source_spec_file": metadata.get("source_spec_file"), "source_spec_file": metadata.get("source_spec_file"),
"scenario_id": metadata.get("scenario_id"),
"semantic_tags": metadata.get("semantic_tags"),
} }
return { return {
"generation_id": generation_id, "generation_id": generation_id,
@ -199,6 +219,7 @@ def build_history_record(
def build_metadata(args: argparse.Namespace, spec: dict[str, Any], spec_path: Path | None) -> dict[str, Any]: def build_metadata(args: argparse.Namespace, spec: dict[str, Any], spec_path: Path | None) -> dict[str, Any]:
semantic_tags = extract_semantic_tags(spec)
return { return {
"assistant_prompt_version": args.assistant_prompt_version, "assistant_prompt_version": args.assistant_prompt_version,
"decomposition_prompt_version": args.decomposition_prompt_version, "decomposition_prompt_version": args.decomposition_prompt_version,
@ -206,6 +227,8 @@ def build_metadata(args: argparse.Namespace, spec: dict[str, Any], spec_path: Pa
"agent_focus": args.agent_focus or spec.get("description") or spec.get("title"), "agent_focus": args.agent_focus or spec.get("description") or spec.get("title"),
"architecture_phase": args.architecture_phase, "architecture_phase": args.architecture_phase,
"source_spec_file": str(spec_path.resolve()) if spec_path else None, "source_spec_file": str(spec_path.resolve()) if spec_path else None,
"scenario_id": str(spec.get("scenario_id") or "").strip() or None,
"semantic_tags": semantic_tags,
} }