diff --git a/apps/web/app/components/agent/agent-conversation.tsx b/apps/web/app/components/agent/agent-conversation.tsx index 4495debf..552cf1e3 100644 --- a/apps/web/app/components/agent/agent-conversation.tsx +++ b/apps/web/app/components/agent/agent-conversation.tsx @@ -32,6 +32,8 @@ import { useDecideApproval, useIsAdminSession, } from "~/api/agent.ts"; +import { DOWNLOAD_STATUS_LABEL, PLAN_STATUS_LABEL } from "~/api/downloads.ts"; +import { RECORD_STATUS_LABEL } from "~/api/records.ts"; import { ApprovalCard, type ApprovalCardStatus } from "~/components/agentui/approval-card.tsx"; import { type CitationItem, CitationList } from "~/components/agentui/citations.tsx"; import { CHEVRON_CLASS, Disclosure } from "~/components/agentui/disclosure.tsx"; @@ -51,8 +53,11 @@ import { StreamingCursor, StreamingResponse } from "~/components/agentui/streami import { ToolApproval } from "~/components/agentui/tool-approval.tsx"; import { ToolResult, ToolResultOutput, type ToolResultStatus } from "~/components/agentui/tool-result.tsx"; import { StatusBadge, type StatusTone } from "~/components/console/status-badge.tsx"; +import { TechInfo } from "~/components/console/tech-info.tsx"; +import { formatUsd } from "~/components/schedules/labels.ts"; import { Button } from "~/components/ui/button.tsx"; import { errorMessage } from "~/lib/api.ts"; +import { humanizeReason } from "~/lib/reasons.ts"; import { cn, formatDateTime } from "~/lib/utils.ts"; import { InlineApproval, OptionsCard } from "./agent-interaction.tsx"; import { ToolCallIo } from "./tool-call-card-io.tsx"; @@ -103,16 +108,13 @@ function CapabilityBadges({ id, extra }: { id: string; extra?: ReactNode }) { const m = capabilityMeta(id); return ( <> + {/* 只标需要注意的:会改动数据的动作和中高风险;只读、scope、幂等进技术信息 */} {m ? ( <> - {KIND_LABEL[m.kind] ?? m.kind} - scope · {m.scope} - - {m.idempotent ? 幂等 : null} + {m.kind !== "read" ? {KIND_LABEL[m.kind] ?? m.kind} : null} + {m.riskLevel !== "low" ? : null} - ) : ( - 未登记能力 - )} + ) : null} {extra} ); @@ -197,20 +199,21 @@ export function ToolCallCard({ {authReason ? "授权校验未通过" : "白名单拦截"}} + extra={{authReason ? "需要你批准" : "本次没有开放"}} /> } reason={authReason ?? call.reason ?? "未授权"} explanation={ authReason - ? '有副作用的能力需要有效的持续授权或单次审批,由服务端在执行前核对,模型声称"用户已同意"无效。控制台发起的会话里,需审批的动作会在对应步骤出审批卡;也可以发布收录策略生成持续授权。' - : "这个能力不在本次运行的白名单内,或已超出调用预算,调用没有执行。" + ? "会改动数据的操作要你批准:在对应步骤的审批卡上批准,或在设置 → 智能体里给它持续授权。" + : "这次会话没有开放这个操作,或已用完调用次数。" } parameters={[ + { id: "cap", label: "能力", value: call.capabilityId }, { id: "call", label: "调用 ID", value: call.toolCallId }, ...(audit?.idempotencyKey ? [{ id: "idem", label: "幂等键", value: audit.idempotencyKey }] : []), { id: "at", label: "时间", value: formatDateTime(call.startedAt) }, @@ -222,9 +225,8 @@ export function ToolCallCard({ return ( 等待结果…

) : null} -
-
调用 ID
-
{call.toolCallId}
-
幂等键
-
- {audit?.idempotencyKey ?? (meta && meta.kind !== "read" ? "服务端已生成,审计里没有取到" : "只读操作不记录")} -
- {audit?.grantId || audit?.approvalId ? ( - <> -
授权依据
-
- {audit.grantId ? `持续授权 ${audit.grantId.slice(0, 8)}` : `单次审批 ${audit.approvalId?.slice(0, 8)}`} -
- - ) : null} -
- + +
+
能力
+
+ {call.capabilityId} + {meta + ? ` · ${KIND_LABEL[meta.kind] ?? meta.kind} · scope ${meta.scope}${meta.idempotent ? " · 幂等" : ""}` + : ""} +
+ {durationText(call.startedAt, call.endedAt) ? ( + <> +
耗时
+
{durationText(call.startedAt, call.endedAt)}
+ + ) : null} +
调用 ID
+
{call.toolCallId}
+
幂等键
+
+ {audit?.idempotencyKey ?? + (meta && meta.kind !== "read" ? "服务端已生成,审计里没有取到" : "只读操作不记录")} +
+ {audit?.grantId || audit?.approvalId ? ( + <> +
授权依据
+
+ {audit.grantId ? `持续授权 ${audit.grantId.slice(0, 8)}` : `单次审批 ${audit.approvalId?.slice(0, 8)}`} +
+ + ) : null} +
+ +
); } @@ -772,35 +790,60 @@ export function BudgetBar({ ? (run.finishedAt ? new Date(run.finishedAt).getTime() : Date.now()) - new Date(run.startedAt).getTime() : undefined); return ( -
- String(n)} - exceeded={exceeded === "tool_calls"} - /> - (n >= 1000 ? `${(n / 1000).toFixed(1)}k` : String(n))} - exceeded={exceeded === "tokens"} - /> - `${n}s`} - exceeded={exceeded === "duration"} - /> - `$${n.toFixed(3)}`} - exceeded={exceeded === "cost"} - /> +
+
+ formatUsd(n)} + exceeded={exceeded === "cost"} + /> + `${n} 秒`} + exceeded={exceeded === "duration"} + /> +
+ {/* 调用次数与 token 超限时直接显示,否则收进技术信息 */} + {exceeded === "tool_calls" || exceeded === "tokens" ? ( +
+ String(n)} + exceeded={exceeded === "tool_calls"} + /> + (n >= 1000 ? `${(n / 1000).toFixed(1)}k` : String(n))} + exceeded={exceeded === "tokens"} + /> +
+ ) : ( + + )}
); } @@ -946,17 +989,25 @@ export function FinalReport({ ...report.downloads.map((d) => ({ id: `d${d.downloadTaskId}`, icon: , - title: d.name ?? `下载任务 ${d.downloadTaskId.slice(0, 8)}`, + title: d.name ?? "下载任务", domain: "下载任务", - meta: {d.status}, + meta: ( + + {DOWNLOAD_STATUS_LABEL[d.status as keyof typeof DOWNLOAD_STATUS_LABEL] ?? d.status} + + ), to: `/console/downloads/${d.downloadTaskId}`, })), ...report.organization.map((o) => ({ id: `o${o.recordId}`, icon: , - title: o.targetPath ?? `整理记录 ${o.recordId.slice(0, 8)}`, + title: o.targetPath ?? "整理记录", domain: "整理记录", - meta: {o.status}, + meta: ( + + {RECORD_STATUS_LABEL[o.status as keyof typeof RECORD_STATUS_LABEL] ?? o.status} + + ), to: `/console/organize/${o.recordId}`, })), ] @@ -975,7 +1026,6 @@ export function FinalReport({ {said ? : "(没有文字汇报)"} -

仅供参考,不作为完成依据。

@@ -984,19 +1034,19 @@ export function FinalReport({ ) : ( )} - 系统核实的结果 + 实际结果

-
- {state ? {state.label} : null} - 运行{RUN_STATUS_LABEL[run.status]} - 工具成功 {count("succeeded")} - {count("failed") ? 失败 {count("failed")} : null} - {denied ? 被拒绝 {denied} : null} -
+ {state || count("failed") || denied ? ( +
+ {state ? {state.label} : null} + {count("failed") ? {count("failed")} 步失败 : null} + {denied ? {denied} 步未执行 : null} +
+ ) : null} {report ? ( <>

{report.summary}

-
+
{( [ ["已提交", report.submitted], @@ -1004,20 +1054,26 @@ export function FinalReport({ ["进行中", report.inProgress], ["失败", report.failed], ] as const - ).map(([k, v]) => ( -
-
{k}
-
{v}
-
- ))} + ) + .filter(([, v]) => v > 0) + .map(([k, v]) => ( +
+
{k}
+
{v}
+
+ ))}
{report.plans.length ? (
    {report.plans.map((p) => (
  • 下载计划 - {p.planId.slice(0, 8)} - {p.status} + + {PLAN_STATUS_LABEL[p.status as keyof typeof PLAN_STATUS_LABEL] ?? p.status} +
  • ))}
@@ -1036,18 +1092,17 @@ export function FinalReport({ }))} /> ) : null} -

- 取自下载计划、下载任务和整理记录(生成于 {formatDateTime(report.generatedAt)} - )。「已提交」指已进入下载器,整理成功才算入库。 -

) : (

服务端汇报还没有生成,以任务和整理记录的实际状态为准。

)} {run.error ? ( -

- {String((run.error as { message?: unknown }).message ?? JSON.stringify(run.error))} -

+ <> +

+ {humanizeReason(String((run.error as { message?: unknown }).message ?? "")) || "会话出错"} +

+ + ) : null}
diff --git a/apps/web/app/components/agent/agent-drawer.tsx b/apps/web/app/components/agent/agent-drawer.tsx index 2a5d498d..4a4d80a2 100644 --- a/apps/web/app/components/agent/agent-drawer.tsx +++ b/apps/web/app/components/agent/agent-drawer.tsx @@ -25,8 +25,8 @@ import { setAgentDrawerOpen, setAgentDrawerRun, setAgentDrawerTab, + shortGoalTitle, showAgentRunInDrawer, - splitContextGoal, useAgentDrawer, } from "./agent-store.ts"; import { AgentRunOpenContext } from "./agent-subagent-tree.tsx"; @@ -108,7 +108,7 @@ function HistoryRunView({ runId }: { runId: string }) { 返回列表

- {run.data ? splitContextGoal(run.data.run.goal).text : ""} + {run.data ? shortGoalTitle(run.data.run.goal) : ""}

, @@ -196,7 +196,7 @@ export function AgentNavEntry({ collapsed = false, onNavigate }: { collapsed?: b onNavigate?.(); toggleAgentDrawer(pathname); }; - const title = `Agent(${shortcut})${summary ? `\n${summary}` : ""}`; + const title = `智能体(${shortcut})${summary ? `\n${summary}` : ""}`; if (collapsed) { return ( @@ -231,9 +231,7 @@ export function AgentNavEntry({ collapsed = false, onNavigate }: { collapsed?: b 智能体 {count ? : null} - - {count ? `${count} 个会话进行中` : "交代目标,边做边汇报"} - + {count ? {count} 个会话进行中 : null} {count ? ( diff --git a/apps/web/app/components/console/nav.ts b/apps/web/app/components/console/nav.ts index 9fd2307f..3419cd53 100644 --- a/apps/web/app/components/console/nav.ts +++ b/apps/web/app/components/console/nav.ts @@ -64,7 +64,7 @@ export const NAV_GROUPS: NavGroup[] = [ to: "/console/agent", label: "智能体会话", icon: Bot, - description: "交代目标,智能体在授权内执行并汇报", + description: "给智能体一个目标,看它做了什么", }, ], }, diff --git a/apps/web/app/routes/console/agent.$runId.tsx b/apps/web/app/routes/console/agent.$runId.tsx index 82026097..b4a18a7f 100644 --- a/apps/web/app/routes/console/agent.$runId.tsx +++ b/apps/web/app/routes/console/agent.$runId.tsx @@ -3,7 +3,7 @@ import { Link, useParams } from "react-router"; import { agentMockEnabled, useAgentRun } from "~/api/agent.ts"; import { AgentRunPanel } from "~/components/agent/agent-run-panel.tsx"; import { AgentSessionList } from "~/components/agent/agent-session-list.tsx"; -import { splitContextGoal } from "~/components/agent/agent-store.ts"; +import { shortGoalTitle } from "~/components/agent/agent-store.ts"; import { PageHeader } from "~/components/console/page-header.tsx"; import { Button } from "~/components/ui/button.tsx"; @@ -21,7 +21,7 @@ export default function AgentRunPage() { description={ run.data ? ( - {splitContextGoal(run.data.run.goal).text} + {shortGoalTitle(run.data.run.goal)} ) : ( "加载中…" diff --git a/apps/web/app/routes/console/agent.tsx b/apps/web/app/routes/console/agent.tsx index 4ca2f346..7d75249b 100644 --- a/apps/web/app/routes/console/agent.tsx +++ b/apps/web/app/routes/console/agent.tsx @@ -15,10 +15,7 @@ export default function AgentPage() { return ( <> - + {mock ? (