feat: give the curator continuous context and durable idea history
This commit is contained in:
+73
-4
@@ -363,12 +363,41 @@ function IdeaDetail({
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
{idea.snapshots && idea.snapshots.length > 1 && (
|
||||
<details className="idea-history">
|
||||
<summary>这个想法走过的路</summary>
|
||||
<div>
|
||||
{[...idea.snapshots].reverse().map((snapshot, index) => (
|
||||
<article key={`${snapshot.created_at}-${index}`}>
|
||||
<time>
|
||||
{new Intl.DateTimeFormat("zh-CN", {
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
}).format(new Date(snapshot.created_at))}
|
||||
</time>
|
||||
<strong>
|
||||
{snapshot.change_kind === "manual_calibration"
|
||||
? "你重新放置了它"
|
||||
: snapshot.motion}
|
||||
</strong>
|
||||
<p>{snapshot.trajectory}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</details>
|
||||
)}
|
||||
|
||||
{idea.fragments && idea.fragments.length > 0 && (
|
||||
<details className="evidence">
|
||||
<summary>构成这个想法的片段</summary>
|
||||
<summary>原始脉络</summary>
|
||||
<div>
|
||||
{idea.fragments.map((fragment) => (
|
||||
<blockquote key={fragment.id}>{fragment.content}</blockquote>
|
||||
<article key={fragment.id}>
|
||||
<time>{dayLabel(fragment.created_at)}</time>
|
||||
<blockquote>{fragment.content}</blockquote>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</details>
|
||||
@@ -454,6 +483,8 @@ const eventNames: Record<string, string> = {
|
||||
tool_search_ideas: "搜索已有想法",
|
||||
tool_inspect_idea: "查看想法轨迹",
|
||||
model_attempt_completed: "模型返回",
|
||||
model_round_completed: "完成一轮判断",
|
||||
convergence_repair_started: "转入收敛判断",
|
||||
validation_failed: "结构校验未通过",
|
||||
decision_committed: "提交分析结果",
|
||||
run_failed: "运行失败",
|
||||
@@ -466,6 +497,7 @@ const auditNames: Record<string, string> = {
|
||||
logout: "退出登录",
|
||||
fragment_created: "保存新片段",
|
||||
idea_position_calibrated: "人工校准位势",
|
||||
agent_run_requeued: "重新分析",
|
||||
debug_sharing_changed: "调整调试共享",
|
||||
};
|
||||
|
||||
@@ -510,6 +542,18 @@ function EventPayload({ event }: { event: AgentEvent }) {
|
||||
</p>
|
||||
);
|
||||
}
|
||||
if (event.event_type === "model_round_completed") {
|
||||
return (
|
||||
<p>
|
||||
输入 {compactNumber(Number(payload.input_tokens ?? 0))} · 输出{" "}
|
||||
{compactNumber(Number(payload.output_tokens ?? 0))} · 推理{" "}
|
||||
{compactNumber(Number(payload.reasoning_tokens ?? 0))}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
if (event.event_type === "convergence_repair_started") {
|
||||
return <p>工具探索没有及时收束,系统改用已有上下文直接完成判断。</p>;
|
||||
}
|
||||
if (event.event_type === "validation_failed") {
|
||||
return (
|
||||
<p>
|
||||
@@ -561,11 +605,24 @@ function RunDetail({
|
||||
onClose: () => void;
|
||||
}) {
|
||||
const [detail, setDetail] = useState<AgentRunDetail | null>(null);
|
||||
const [retrying, setRetrying] = useState(false);
|
||||
const [requeued, setRequeued] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
api.adminRun(runId).then(setDetail);
|
||||
}, [runId]);
|
||||
|
||||
async function retry() {
|
||||
if (retrying || requeued) return;
|
||||
setRetrying(true);
|
||||
try {
|
||||
await api.retryAdminRun(runId);
|
||||
setRequeued(true);
|
||||
} finally {
|
||||
setRetrying(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="sheet-backdrop" role="presentation" onMouseDown={onClose}>
|
||||
<article
|
||||
@@ -614,8 +671,7 @@ function RunDetail({
|
||||
<blockquote>{detail.fragment.content}</blockquote>
|
||||
) : (
|
||||
<div className="redacted-content">
|
||||
内容已隔离 · {detail.fragment.content_length} 字 · 指纹{" "}
|
||||
{detail.fragment.content_sha256}
|
||||
内容已隔离 · {detail.fragment.content_length} 字
|
||||
</div>
|
||||
)}
|
||||
<small>{detail.privacy.reason}</small>
|
||||
@@ -636,6 +692,19 @@ function RunDetail({
|
||||
</article>
|
||||
))}
|
||||
</section>
|
||||
{detail.run.status === "error" && (
|
||||
<button
|
||||
className="retry-run"
|
||||
onClick={() => void retry()}
|
||||
disabled={retrying || requeued}
|
||||
>
|
||||
{requeued
|
||||
? "已经重新交给策展者"
|
||||
: retrying
|
||||
? "正在重新交付…"
|
||||
: "重新整理这条记录"}
|
||||
</button>
|
||||
)}
|
||||
<footer className="reasoning-boundary">
|
||||
不保存模型隐藏思维链;这里展示的是输入、工具行为、用量、校验与最终结构化产物。
|
||||
</footer>
|
||||
|
||||
@@ -12,12 +12,17 @@ export type User = {
|
||||
};
|
||||
|
||||
export type Snapshot = {
|
||||
title: string | null;
|
||||
summary: string | null;
|
||||
maturity_ai: number;
|
||||
maturity_override: number | null;
|
||||
confidence: number | null;
|
||||
motion: string;
|
||||
position: string;
|
||||
tension: string;
|
||||
trajectory: string;
|
||||
possible_moves: string[];
|
||||
change_kind: "analysis" | "manual_calibration" | "legacy_partial";
|
||||
created_at: string;
|
||||
};
|
||||
|
||||
@@ -226,6 +231,10 @@ export const api = {
|
||||
request<{ items: AgentRun[] }>(`/api/admin/runs?limit=${limit}`),
|
||||
adminRun: (id: string) =>
|
||||
request<AgentRunDetail>(`/api/admin/runs/${id}`),
|
||||
retryAdminRun: (id: string) =>
|
||||
request<{ queued: boolean }>(`/api/admin/runs/${id}/retry`, {
|
||||
method: "POST",
|
||||
}),
|
||||
adminAudit: (limit = 80) =>
|
||||
request<{ items: AuditEvent[] }>(`/api/admin/audit?limit=${limit}`),
|
||||
};
|
||||
|
||||
+57
-3
@@ -885,12 +885,18 @@ summary:focus-visible {
|
||||
color: var(--warm);
|
||||
}
|
||||
|
||||
.idea-history,
|
||||
.evidence {
|
||||
margin-top: 50px;
|
||||
border-top: 1px solid var(--line);
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.idea-history + .evidence {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.idea-history summary,
|
||||
.evidence summary {
|
||||
padding: 18px 0;
|
||||
cursor: pointer;
|
||||
@@ -898,12 +904,44 @@ summary:focus-visible {
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.idea-history article {
|
||||
display: grid;
|
||||
grid-template-columns: 92px 1fr;
|
||||
gap: 4px 14px;
|
||||
padding: 14px 0;
|
||||
border-bottom: 1px solid rgba(23, 37, 31, 0.07);
|
||||
}
|
||||
|
||||
.idea-history time,
|
||||
.evidence time {
|
||||
color: #92958f;
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
.idea-history strong {
|
||||
color: var(--ink);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.idea-history p {
|
||||
grid-column: 2;
|
||||
margin: 2px 0 0;
|
||||
color: #626b66;
|
||||
font-size: 11px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.evidence article {
|
||||
padding: 13px 0;
|
||||
border-bottom: 1px solid rgba(23, 37, 31, 0.07);
|
||||
}
|
||||
|
||||
.evidence blockquote {
|
||||
margin: 0;
|
||||
padding: 15px 0;
|
||||
margin: 5px 0 0;
|
||||
padding: 0;
|
||||
color: #4e5752;
|
||||
font: 400 14px/1.85 "Songti SC", "STSong", serif;
|
||||
border-bottom: 1px solid rgba(23, 37, 31, 0.07);
|
||||
}
|
||||
|
||||
.admin-view {
|
||||
@@ -1411,6 +1449,22 @@ summary:focus-visible {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.retry-run {
|
||||
margin-top: 28px;
|
||||
padding: 9px 13px;
|
||||
border: 1px solid rgba(153, 104, 72, 0.28);
|
||||
border-radius: 4px;
|
||||
background: transparent;
|
||||
color: var(--warm);
|
||||
cursor: pointer;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.retry-run:disabled {
|
||||
cursor: default;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.reasoning-boundary {
|
||||
margin-top: 45px;
|
||||
padding-top: 16px;
|
||||
|
||||
Reference in New Issue
Block a user