feat(web): W4-F rebuild agent conversation and drawer with agentui components
- Port agentui (MIT, ashish200729/agentui@b2b752b) Message/MessageBubble/MessageScroller, StreamingResponse, ToolResult/ToolApproval, TodoList, ApprovalCard, CodeBlock/FileDiff, Citations, PromptInput into components/agentui; drop motion/shiki/Next, switch colors, type scale, radii, durations and easing to semantic tokens, honour reduced motion - Conversation: streaming cursor, collapsible thinking, tool cards with kind/scope/risk/ idempotency badges and audit idempotency key, prominent server-rejection state for both whitelist rejections and unauthorized errors, compaction markers, budget meters with over-limit state, approval cards (admin session only), two-column final report (model said vs system verified with task/record links), follow-scroll with jump-to-bottom - Composer: Enter send / Shift+Enter newline, stop = abort (confirmed), append, execute/plan_only switch, "execute this plan" from finished plan_only runs - Session list sidebar with status filter and usage; reconnect/error banners with retry - Drawer: route-derived context (entity/site/task), minimize to a live mini dock - Fix stream refetch race when the run switches from loading to finished Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
29cd10b27e
commit
8352e5e881
26 files changed
+3390
-585
No files matched your search
@@ -1,5 +1,5 @@
|
||||
import { type AgentRunEvent, type AgentUsageTotals, EventType } from "@umt/contracts";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { agentMockEnabled, fetchRunEvents } from "~/api/agent.ts";
|
||||
import { sse } from "~/lib/sse.ts";
|
||||
|
||||
@@ -40,6 +40,8 @@ export type AgentStreamState = {
|
||||
finished: boolean;
|
||||
error: string | null;
|
||||
connected: "sse" | "poll" | "mock" | "idle";
|
||||
/** 读取失败后手动重试(从最后收到的 seq 之后补读) */
|
||||
retry: () => void;
|
||||
};
|
||||
|
||||
const SSE_TYPES: string[] = [EventType.agentTextDelta, EventType.agentToolCall, EventType.agentRunStatus];
|
||||
@@ -177,9 +179,8 @@ export function useAgentStream(runId: string | undefined, active: boolean): Agen
|
||||
const [events, setEvents] = useState<AgentRunEvent[]>([]);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [connected, setConnected] = useState<AgentStreamState["connected"]>("idle");
|
||||
const [nonce, setNonce] = useState(0);
|
||||
const lastSeq = useRef(-1);
|
||||
const inflight = useRef(false);
|
||||
const pending = useRef(false);
|
||||
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: 换 run 时重置
|
||||
useEffect(() => {
|
||||
@@ -188,10 +189,14 @@ export function useAgentStream(runId: string | undefined, active: boolean): Agen
|
||||
lastSeq.current = -1;
|
||||
}, [runId]);
|
||||
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: nonce 变化 = 手动重试,重新建立补读
|
||||
useEffect(() => {
|
||||
if (!runId) return;
|
||||
let stopped = false;
|
||||
const ctrl = new AbortController();
|
||||
// 在途/待补标记按本次 effect 独立:active 变化重建 effect 时,被中止的旧请求不会把新一轮的补读挡在门外
|
||||
const inflight = { current: false };
|
||||
const pending = { current: false };
|
||||
|
||||
const pull = async () => {
|
||||
if (inflight.current) {
|
||||
@@ -243,12 +248,13 @@ export function useAgentStream(runId: string | undefined, active: boolean): Agen
|
||||
unsubscribe?.();
|
||||
setConnected("idle");
|
||||
};
|
||||
}, [runId, active]);
|
||||
}, [runId, active, nonce]);
|
||||
|
||||
const retry = useCallback(() => setNonce((n) => n + 1), []);
|
||||
return useMemo(() => {
|
||||
const items = foldEvents(events);
|
||||
const end = [...events].reverse().find((e) => e.type === "run_completed" || e.type === "run_failed");
|
||||
const usage = end && (end.type === "run_completed" || end.type === "run_failed") ? end.usage : null;
|
||||
return { events, items, usage, finished: !!end, error, connected };
|
||||
}, [events, error, connected]);
|
||||
return { events, items, usage, finished: !!end, error, connected, retry };
|
||||
}, [events, error, connected, retry]);
|
||||
}
|
||||
Reference in new issue
Block a user