import type { TechniqueAuditRow, TechniqueAuditStatus } from "./consultation-agent-events.ts"; export type SpokenAnswerAndTechniqueAudit = Readonly<{ spoken: string; rows: readonly TechniqueAuditRow[]; complete: boolean; }>; const AUDIT_HEADER = /^\|\s*技法\s*\|\s*状态\s*\|\s*说明\s*\|?\s*$/; const AUDIT_SEPARATOR = /^\|?(?:\s*:?-{2,}:?\s*\|){2,}\s*:?-{2,}:?\s*\|?\s*$/; const MACHINE_KEY = /^[a-z][a-z0-9_]{2,}$/i; const STATUS_LABELS: Readonly> = { executed: "已执行", blocked: "阻塞", not_applicable: "不适用", }; const TECHNIQUE_LABELS: Readonly> = { "VedAstro Cloud State": "VedAstro 云状态", "VedAstro Raw Archive Manifest": "VedAstro 原始档案清单", "External Engine Cross-Validation": "外部引擎交叉验证", "Western Cross-Validation": "西洋交叉验证", "Cross-System Arbitration": "跨系统仲裁", "Evidence Packet": "证据包", "Blind Technical Mode": "盲技术模式", "MEVG / Global Web Evidence": "MEVG / 全网证据", "Real Case Calibration": "真实案例校准", "Timing Precision Gate": "时间精度门", "Functional Benefic/Malefic": "功能吉凶星", "Formal Vargas D1–D60": "正式分盘 D1–D60", "Research D-N through D60": "研究型 D-N 至 D60", "Extended Vargas D81/D108/D144": "扩展分盘 D81/D108/D144", "Arudha / UL / A10": "Arudha / UL / A10", "Narayana Dasha": "Narayana Dasha", "Vimshottari sub-periods": "Vimshottari 小运", Yogas: "格局 Yoga", "Chara Dasha": "Chara Dasha", "Transits / Sade Sati": "行运 / 土星过月", Ashtakavarga: "Ashtakavarga", "KP cusps": "KP 宫头", "Gulika / Mandi": "Gulika / Mandi", "Shadbala components": "Shadbala 分量", "Ashtakavarga Kakshya": "Ashtakavarga Kakshya", "Western natal (tropical)": "西洋本命(回归黄道)", "Prashna chart": "问卦盘(Prashna)", "Ashtakoota matching": "合盘 Ashtakoota", "Adhana / Niseka": "Adhana / Niseka", "Muhurta election": "择时 Muhurta", }; const WESTERN_LAYER_LABELS: Readonly> = { transits: "行运", solar_return: "太阳回归盘", secondary_progressions: "次限", solar_arc_directions: "太阳弧", converse_secondary_progressions: "逆行次限", converse_solar_arc_directions: "逆行太阳弧", midpoints: "中点", lunar_return: "月亮回归盘", transit_duration_scan: "行运时长扫描", parans: "parans", }; const NOTE_LABELS: Readonly> = { confidence_capped_without_verified_official_cloud: "未经核验的官方云证据,置信度封顶", official_raw_archive_not_auditable_for_this_run: "官方原始档案本轮不可审计", official_raw_archive_is_auditable: "官方原始档案可审计", official_cloud_evidence_available: "官方云证据可用", foreground_optional_evidence_timeout: "前台官方云证据超时,本轮未赶上", gateway_invocation_error: "官方云网关调用失败", claims_capped_until_pyjhora_jhora_jyotishganit_are_invoked_for_this_run: "结论上限受外部引擎调用限制", three_engine_runtime_closure_available: "三引擎运行时已闭环", complete_packet_required_for_high_confidence: "高置信度需完整证据包", supports_high_confidence: "支持高置信度", normal_runtime_mode: "常规运行模式", prevents_conversation_feedback_leakage: "防止会话反馈泄漏", caps_claims_until_global_web_evidence_runs: "全局网页证据未运行,声明受限", partial_reference_only_until_outcome_replay: "仅部分参考,未做结果回放", supports_calibration: "支持校准", blocks_false_precision_until_control_date_rankings_pass: "控制日期排名未通过前禁止伪精度", high_rigor_claims_blocked_until_functional_nature_layer_is_present: "功能属性与自然属性冲突时降置信", "observation for A10/UL; commercial claims stay non-deterministic": "仅观察层,商业断语保持非确定", "component parity remains partial": "分量对齐仍为部分", "KP remains non-deterministic in this product": "本产品内 KP 非确定性", observation_only: "仅观察层", "Chesta/Sthana/Dig/Drik/Kala/Naisargika as components; absolute Virupa truth is not closed": "绝对 Virupa 真值未闭环", "observation_only; not verified day/month timing": "仅观察层,不作已核验日期", "observation windows, not guaranteed events": "观察窗口,不是保证事件", "commercial claims stay non-deterministic; not a majority-vote truth": "商业断语保持非确定,不以多数引擎表决", "this run is a natal consultation": "本轮为本命咨询", horary_path: "问卦路径", "requires a partner chart": "需要伴侣盘", "research_only; not a natal consultation layer": "仅研究用途", "not an electional request": "非择时请求", layer_not_materialized: "本层未物化", }; export function techniqueAuditStatusLabel(status: TechniqueAuditStatus): string { return STATUS_LABELS[status]; } export function displayTechniqueAuditLabel(technique: string): string { const mapped = TECHNIQUE_LABELS[technique]; if (mapped) return mapped; const western = /^Western (.+)$/i.exec(technique); if (!western?.[1]) return technique; const layer = western[1].trim(); const layerLabel = WESTERN_LAYER_LABELS[layer] ?? layer.replaceAll("_", " "); return `西洋${layerLabel}`; } export function displayTechniqueAuditNote(note: string | undefined): string { const trimmed = note?.trim() ?? ""; if (!trimmed) return ""; const mapped = NOTE_LABELS[trimmed]; if (mapped) return mapped; const formal = /^(\d+)\/20 named traditional charts/i.exec(trimmed); if (formal) return `${formal[1]}/20 传统命名分盘`; const research = /^(\d+) generic D-N charts/i.exec(trimmed); if (research) return `${research[1]} 张通用分盘,非传统命名`; const extended = /^(\d+) super-divisional charts/i.exec(trimmed); if (extended) return `${extended[1]} 张超细分盘`; if (MACHINE_KEY.test(trimmed) || trimmed.includes("_")) return ""; return trimmed; } export function parseTechniqueAuditStatus(value: string): TechniqueAuditStatus { const normalized = value.trim().toLowerCase(); if ( value.trim() === "已执行" || ["used", "executed", "complete", "official_verified", "pass", "ready", "ok", "partial"].includes(normalized) ) { return "executed"; } if ( value.trim() === "不适用" || ["not_applicable", "n/a", "na", "not applicable"].includes(normalized) ) { return "not_applicable"; } return "blocked"; } export function normalizeTechniqueAuditRows(value: unknown): TechniqueAuditRow[] | undefined { if (!Array.isArray(value)) return undefined; const rows: TechniqueAuditRow[] = []; for (const item of value.slice(0, 80)) { if (!item || typeof item !== "object" || Array.isArray(item)) continue; const record = item as Record; const technique = String(record.technique ?? record.name ?? "").trim(); if (!technique) continue; const noteValue = record.note ?? record.boundary ?? record.effect_on_confidence; const note = typeof noteValue === "string" ? noteValue.trim().slice(0, 320) : ""; rows.push({ technique: technique.slice(0, 160), status: parseTechniqueAuditStatus(String(record.status ?? "")), ...(note ? { note } : {}), }); } return rows.length > 0 ? rows : undefined; } export function splitSpokenAnswerAndTechniqueAudit(text: string): SpokenAnswerAndTechniqueAudit { const lines = text.replace(/\r\n/g, "\n").split("\n"); let headerIndex = -1; for (let index = 0; index < lines.length; index += 1) { if (AUDIT_HEADER.test(lines[index].trim())) headerIndex = index; } if (headerIndex < 0) { return { spoken: text.replace(/\s+$/, ""), rows: [], complete: true }; } const prefix = lines.slice(0, headerIndex).join("\n").replace(/\s+$/, ""); const tableLines = lines.slice(headerIndex).map((line) => line.trim()); const separator = tableLines[1] && AUDIT_SEPARATOR.test(tableLines[1]) ? tableLines[1] : null; if (!separator) { return { spoken: prefix, rows: [], complete: false }; } const rows: TechniqueAuditRow[] = []; for (const line of tableLines.slice(2)) { if (!line) break; if (!line.startsWith("|")) break; const cells = splitGfmRow(line); if (cells.length < 3) return { spoken: prefix, rows, complete: false }; const technique = cells[0]?.trim() ?? ""; if (!technique) continue; const note = cells.slice(2).join(" | ").trim().slice(0, 320); rows.push({ technique: technique.slice(0, 160), status: parseTechniqueAuditStatus(cells[1] ?? ""), ...(note ? { note } : {}), }); } if (rows.length === 0) { return { spoken: prefix, rows: [], complete: false }; } return { spoken: text.replace(/\s+$/, ""), rows, complete: true }; } export function resolveTechniqueAuditRows( split: SpokenAnswerAndTechniqueAudit, delivered?: readonly TechniqueAuditRow[], ): readonly TechniqueAuditRow[] { if (split.complete && split.rows.length > 0) return split.rows; return delivered && delivered.length > 0 ? delivered : []; } function splitGfmRow(line: string): string[] { return line.replace(/^\|/, "").replace(/\|$/, "").split("|").map((cell) => cell.trim()); }