merge: synchronize remote main

This commit is contained in:
Jesse_Chen
2026-07-21 22:57:50 +08:00
130 changed files with 10441 additions and 553 deletions
@@ -145,20 +145,10 @@ function TerminalAction({ controller, error, path }: {
readonly error: string;
readonly path: NonNullable<ReturnType<typeof guidedTerminalPath>>;
}) {
if (path.kind === "complete_with_candidate") {
return (
<div className="birth-time-next-step">
<b></b>
<p>使 {path.time} <span className="phrase-nowrap"></span><span className="phrase-nowrap"></span><span className="phrase-nowrap"></span><span className="phrase-nowrap"></span></p>
<button className="button-primary birth-time-guided-action" disabled={controller.pending} onClick={() => controller.completeCandidate(path.time)} type="button">
{controller.pending ? `正在采用 ${path.time}` : `采用 ${path.time} 并进入对话`}
</button>
{error ? <p className="form-error" role="alert">{error}</p> : null}
</div>
);
}
return (
<div className="birth-time-new-assessment">
<b></b>
<p></p>
<button className="button-secondary birth-time-guided-action" disabled={controller.pending} onClick={controller.editBirthTimeDetails} type="button"></button>
{error ? <p className="form-error" role="alert">{error}</p> : null}
<small></small>
+4 -1
View File
@@ -1,5 +1,8 @@
import { ChatMessageContent } from "@/components/chat-message-content";
import { ClaimBoundaryBadge } from "@/components/claim-boundary-badge";
import { EvidenceAuditPanel } from "@/components/evidence-audit-panel";
import type { ChatMessageView } from "@/lib/chat-message-view";
import { consultationReportMarkdown, downloadMarkdownReport } from "@/lib/consultation-report-export";
export function AgentAvatar() {
return <span className="agent-avatar" aria-hidden="true" />;
@@ -23,7 +26,7 @@ export function ChatMessageRow({ message }: { readonly message: ChatMessageView
{message.role === "assistant" ? (
message.state === "thinking"
? <div className="thinking"><i /><i /><i /></div>
: <ChatMessageContent text={message.text} />
: <><ClaimBoundaryBadge status={message.techniqueTruth} /><EvidenceAuditPanel claimStatus={message.techniqueTruth} workflowReceipt={message.workflowReceipt} /><ChatMessageContent text={message.text} /><button className="report-download-button" type="button" onClick={() => downloadMarkdownReport("Jyotisha 咨询报告", consultationReportMarkdown({ title: "Jyotisha 咨询报告", messages: [message] }))}></button></>
) : <p>{message.text}</p>}
</div>
</div>
@@ -0,0 +1,13 @@
const boundaryCopy: Record<string, string> = {
unknown: "证据边界未知",
partial: "部分证据闭环",
observation_only: "仅观察",
blocked: "证据阻塞",
reference_only: "仅参考",
};
export function ClaimBoundaryBadge({ status }: { readonly status?: string }) {
const normalized = status?.trim() || "unknown";
const label = boundaryCopy[normalized] ?? `证据状态:${normalized}`;
return <p className="claim-boundary-badge">{label} · </p>;
}
@@ -0,0 +1,64 @@
type AuditRow = {
label: string;
status: "required" | "partial" | "blocked";
boundary: string;
};
type WorkflowReceipt = {
route: string;
status: string;
preciseTiming: string;
missingLayers: readonly string[];
};
const defaultAuditRows: AuditRow[] = [
{ label: "D1 / Natal", status: "required", boundary: "基础命盘层" },
{ label: "Varga: D9 / D10 / D2 / D11", status: "required", boundary: "按问题域调用,不混用" },
{ label: "Vimshottari Dasha", status: "required", boundary: "阶段证据之一" },
{ label: "Narayana Dasha", status: "required", boundary: "应期/校时必须交叉" },
{ label: "Transit / Gochara", status: "partial", boundary: "精确日/月仍需 holdout" },
{ label: "Shadbala / Ashtakavarga", status: "partial", boundary: "组件 parity 未全闭环" },
{ label: "Functional Benefic/Malefic", status: "required", boundary: "不能只看自然吉凶" },
{ label: "MEVG / Real Case Calibration", status: "blocked", boundary: "外部校准不足时降级" },
];
function workflowRows(receipt?: WorkflowReceipt): AuditRow[] {
if (!receipt) return defaultAuditRows;
return [
{ label: `Workflow route: ${receipt.route}`, status: receipt.status === "blocked" ? "blocked" : "required", boundary: "后端实际路由" },
{ label: `Precise timing: ${receipt.preciseTiming}`, status: receipt.preciseTiming === "allowed" ? "partial" : "blocked", boundary: "精确应期 claim gate" },
{
label: "Missing route layers",
status: receipt.missingLayers.length ? "blocked" : "required",
boundary: receipt.missingLayers.length ? receipt.missingLayers.join(" / ") : "none",
},
...defaultAuditRows,
];
}
export function EvidenceAuditPanel({ claimStatus, workflowReceipt }: { readonly claimStatus?: string; readonly workflowReceipt?: WorkflowReceipt }) {
const rows = workflowRows(workflowReceipt);
const rawReceipt = {
techniqueTruth: claimStatus || "unknown",
workflowReceipt: workflowReceipt ?? null,
privacyBoundary: "no_birth_data_or_private_case_raw",
};
return (
<details className="evidence-audit-panel">
<summary> · {claimStatus || "unknown"}</summary>
<div className="evidence-audit-table" role="table" aria-label="Technique Audit Table">
{rows.map((row) => (
<div className="evidence-audit-row" role="row" key={row.label}>
<span role="cell">{row.label}</span>
<b role="cell" data-status={row.status}>{row.status}</b>
<small role="cell">{row.boundary}</small>
</div>
))}
</div>
<details className="raw-evidence-receipt">
<summary> raw receipt</summary>
<pre>{JSON.stringify(rawReceipt, null, 2)}</pre>
</details>
</details>
);
}
@@ -0,0 +1,23 @@
export type ParameterFreezeRow = {
label: string;
value: string;
};
export function ParameterFreezePanel({ rows }: { readonly rows: readonly ParameterFreezeRow[] }) {
return (
<section className="parameter-freeze-panel" aria-label="当前排盘参数冻结">
<div>
<b></b>
<span></span>
</div>
<dl>
{rows.map((row) => (
<div key={row.label}>
<dt>{row.label}</dt>
<dd>{row.value}</dd>
</div>
))}
</dl>
</section>
);
}