feat(agent): support Responses steering and project-aware tools
Use the dedicated TJWATER_AGENT_URL and Responses stream protocol. Keep running turns active while users steer or queue follow-ups and edit pending delivery. Refresh the model catalog before generation and preserve approval mode selection. Integrate scoped investigation messaging/presentation, geographic place selection, current Agent composer styles and supporting workbench contracts. Validation: 77 focused Vitest checks, 21 browser cases, lint, typecheck and production build. Preserve direct Server/Agent routing and project authentication.
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
import { Mail } from "lucide-react";
|
||||
import { Disclosure } from "@/shared/ui/disclosure";
|
||||
import { Button } from "@/shared/ui/button";
|
||||
import type { AgentMailboxMessage } from "../api/agent-messages";
|
||||
|
||||
const messageStatusLabels = { pending: "待接收", enqueued: "已排队", delivered: "已投递", failed: "投递失败" };
|
||||
|
||||
export function AgentMessageInbox({ messages, loading, error, hasMore, loadingMore, onLoadMore, onRefresh }: {
|
||||
messages: AgentMailboxMessage[];
|
||||
loading?: boolean;
|
||||
error?: string | null;
|
||||
hasMore?: boolean;
|
||||
loadingMore?: boolean;
|
||||
onLoadMore?: () => Promise<void>;
|
||||
onRefresh?: () => void;
|
||||
}) {
|
||||
if (!messages.length && !loading && !error) return null;
|
||||
const received = messages.filter((message) => message.direction === "to_chat").length;
|
||||
return <Disclosure label={<span className="flex items-center gap-2 px-3"><Mail size={14} />任务消息{received ? ` · 收到 ${received} 条` : ""}</span>} initiallyOpen className="surface-reading rounded-xl">
|
||||
<div className="space-y-3 p-3 text-xs" aria-live="polite">
|
||||
<p className="text-[var(--ink-muted)]">新消息会在此显示。由你决定是否继续发送指令。</p>
|
||||
{loading ? <p className="text-[var(--ink-muted)]">正在加载任务消息…</p> : null}
|
||||
{error ? <div className="flex items-center justify-between gap-2"><p className="text-[var(--status-warning-foreground)]">{error}</p><Button variant="ghost" size="sm" onClick={onRefresh}>重试</Button></div> : null}
|
||||
{messages.map((message) => <article key={message.message_id} className="space-y-1.5 border-t border-[var(--surface-divider)] pt-3">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2 text-[var(--ink-muted)]">
|
||||
<span>{message.direction === "to_chat" ? "调查任务来信" : "发给调查任务"}</span>
|
||||
<span>{messageStatusLabels[message.status]}</span>
|
||||
</div>
|
||||
<p className="whitespace-pre-wrap break-words text-sm leading-6 text-[var(--ink)]">{message.text}</p>
|
||||
<p className="break-all text-[var(--ink-muted)]">task_id: <span className="select-text font-mono">{message.task_id}</span></p>
|
||||
<p className="break-all text-[var(--ink-muted)]">run_id: <span className="select-text font-mono">{message.run_id}</span></p>
|
||||
{message.failure_detail ? <p className="break-words text-[var(--status-danger-foreground)]">{message.failure_detail}</p> : null}
|
||||
</article>)}
|
||||
{hasMore ? <Button variant="ghost" size="sm" disabled={loadingMore} onClick={() => void onLoadMore?.().catch(() => undefined)}>{loadingMore ? "正在加载…" : "加载更多消息"}</Button> : null}
|
||||
</div>
|
||||
</Disclosure>;
|
||||
}
|
||||
Reference in New Issue
Block a user