feat(memory): P1 长期记忆升级 —— 异步攒批 Consolidate + 软删 + importance/last_seen #1
@@ -13,12 +13,14 @@ export function ApprovalBar({ run }: { run: RunState }) {
|
|||||||
? pendingApproval(run.exec) ?? { node: "", title: run.detail || "人工审批", summary: "" }
|
? pendingApproval(run.exec) ?? { node: "", title: run.detail || "人工审批", summary: "" }
|
||||||
: null;
|
: null;
|
||||||
if (!approval || !run.taskId) return null;
|
if (!approval || !run.taskId) return null;
|
||||||
return <Bar taskId={run.taskId} node={approval.node} title={approval.title} summary={approval.summary} />;
|
// key=taskId:换任务时重新挂载,重置 submitted 等本地状态,避免上一个审批的「已提交」残留。
|
||||||
|
return <Bar key={run.taskId} taskId={run.taskId} node={approval.node} title={approval.title} summary={approval.summary} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Bar({ taskId, node, title, summary }: { taskId: string; node: string; title: string; summary: string }) {
|
function Bar({ taskId, node, title, summary }: { taskId: string; node: string; title: string; summary: string }) {
|
||||||
const [note, setNote] = useState("");
|
const [note, setNote] = useState("");
|
||||||
const [busy, setBusy] = useState<"approve" | "reject" | null>(null);
|
const [busy, setBusy] = useState<"approve" | "reject" | null>(null);
|
||||||
|
const [submitted, setSubmitted] = useState(false); // 决定已发出;正常情况下任务续跑后本条随 waiting 解除自动消失
|
||||||
const [err, setErr] = useState("");
|
const [err, setErr] = useState("");
|
||||||
|
|
||||||
const decide = async (approved: boolean) => {
|
const decide = async (approved: boolean) => {
|
||||||
@@ -26,8 +28,10 @@ function Bar({ taskId, node, title, summary }: { taskId: string; node: string; t
|
|||||||
setErr("");
|
setErr("");
|
||||||
try {
|
try {
|
||||||
await approveTask(taskId, approved, { node, note });
|
await approveTask(taskId, approved, { node, note });
|
||||||
|
setSubmitted(true); // 关键:成功后置「已提交」而非停在 spinner —— 否则任务若恢复不了会永远卡「提交中…」
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setErr((e as Error).message);
|
setErr((e as Error).message);
|
||||||
|
} finally {
|
||||||
setBusy(null);
|
setBusy(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -38,9 +42,12 @@ function Bar({ taskId, node, title, summary }: { taskId: string; node: string; t
|
|||||||
<ShieldCheck className="h-4 w-4 text-amber-400" strokeWidth={2.2} />
|
<ShieldCheck className="h-4 w-4 text-amber-400" strokeWidth={2.2} />
|
||||||
<span className="font-semibold">人工审批</span>
|
<span className="font-semibold">人工审批</span>
|
||||||
<span className="text-amber-300/80">{title}</span>
|
<span className="text-amber-300/80">{title}</span>
|
||||||
<Badge tone="warn">等待决定</Badge>
|
<Badge tone="warn">{submitted ? "已提交决定" : "等待决定"}</Badge>
|
||||||
</div>
|
</div>
|
||||||
{summary && <pre className="max-h-20 overflow-auto whitespace-pre-wrap font-mono text-[11px] leading-relaxed text-amber-100/80">{summary}</pre>}
|
{summary && <pre className="max-h-20 overflow-auto whitespace-pre-wrap font-mono text-[11px] leading-relaxed text-amber-100/80">{summary}</pre>}
|
||||||
|
{submitted ? (
|
||||||
|
<p className="text-[11px] text-amber-200/80">决定已发出,等待任务续跑…(若长时间无响应,该任务可能已失效)</p>
|
||||||
|
) : (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<input
|
<input
|
||||||
value={note}
|
value={note}
|
||||||
@@ -57,6 +64,7 @@ function Bar({ taskId, node, title, summary }: { taskId: string; node: string; t
|
|||||||
<X className="h-3.5 w-3.5" /> {busy === "reject" ? "提交中…" : "拒绝"}
|
<X className="h-3.5 w-3.5" /> {busy === "reject" ? "提交中…" : "拒绝"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
{err && <p className="text-[11px] text-danger">提交失败:{err}</p>}
|
{err && <p className="text-[11px] text-danger">提交失败:{err}</p>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user