feat(web): W4-A wire console to real endpoints

- Agent page uses contracts (AgentRun/AgentRunDetail/AgentGoalReport) instead of a local
  mirror; ?mock=1 stays as an explicit demo switch; run snapshot refreshes on agent.run_status;
  stream auto-follow only while the user stays near the bottom (abort stays reachable)
- settings: Pi provider select incl. fake, new Agent runtime card (budgets, fakeScript)
- download trace uses /tasks?subjectId= and /organization-records?downloadTaskId= instead of
  time-window engine run matching and path matching; new business-task tab with cancel/retry
- workbench download dialog posts /acquisition-plans and shows per-slot rejection reasons
- preference tag revision history; subscription search by title/kind; identity/alias/subscription
  lists show entity titles; slot cards show current asset summary; policy preview sample titles
- DecisionSummary / IdentityRevisionList / ReleaseIdentityResult / TaskAccepted / PolicyPreviewSummary /
  OrganizePlanArtifactContent taken from contracts
- docs: SSE for console (cookie) vs GET /events?after=&limit= JSON replay for API keys

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-27 21:32:11 +08:00
1 parent 34e8751b45
commit 1023642ef9
30 files changed
+703 -271

No files matched your search

@@ -7,6 +7,7 @@ import {
useLiftSuppression,
usePreferenceTags,
useSuppressions,
useTagRevisions,
useUpdateTag,
} from "~/api/preferences.ts";
import { ConfirmDialog } from "~/components/console/confirm-dialog.tsx";
@@ -315,6 +316,9 @@ export default function PreferencesPage() {
<DetailSection title="证据">
<JsonView value={Object.keys(selected.evidence).length ? selected.evidence : null} />
</DetailSection>
<DetailSection title="修订历史">
<TagRevisions id={selected.id} />
</DetailSection>
</>
) : null}
</DetailSheet>
@@ -324,6 +328,44 @@ export default function PreferencesPage() {
);
}
const REV_ACTION = { created: "创建", updated: "修改", deleted: "删除" } as const;
const FIELD_LABEL: Record<string, string> = {
label: "名称",
kind: "类型",
scope: "作用范围",
rule: "规则",
description: "说明",
evidence: "证据",
status: "状态",
};
function TagRevisions({ id }: { id: string }) {
const q = useTagRevisions(id);
if (q.isPending) return <LoadingState rows={2} />;
if (q.isError) return <ErrorState plain error={q.error} onRetry={() => q.refetch()} />;
if (q.data.length === 0)
return <p className="text-caption text-muted-foreground">没有修订记录(W4 之前创建的标签不带历史)。</p>;
return (
<ul className="space-y-2">
{q.data.map((r) => (
<li key={r.id} className="rounded-lg border border-border/70 px-3 py-2 text-caption">
<div className="flex flex-wrap items-center gap-2">
<span className="font-medium text-foreground">{REV_ACTION[r.action]}</span>
{r.revision ? <span className="text-muted-foreground tabular-nums">r{r.revision}</span> : null}
<span className="text-muted-foreground">{r.actorType === "api_key" ? "API Key" : "用户"}</span>
<span className="ml-auto text-meta text-muted-foreground tabular-nums">{formatDateTime(r.at)}</span>
</div>
<p className="mt-1 text-meta text-muted-foreground">
{r.changedFields.length
? `改动:${r.changedFields.map((f) => FIELD_LABEL[f] ?? f).join("、")}`
: `名称:${r.snapshot.label} · 状态:${r.snapshot.status}`}
</p>
</li>
))}
</ul>
);
}
function SuppressionRow({
s,
label,