Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 7 additions & 47 deletions crates/agent-gateway/web/src/app/GatewayApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ import type {
} from "@liveagent/ui/components/chat/MentionComposer";
import { type NotifyItem, NotifyToast } from "@liveagent/ui/components/chat/NotifyToast";
import { SharedHistoryManagerModal } from "@liveagent/ui/components/chat/SharedHistoryManagerModal";
import { TaskProgressIndicator } from "@liveagent/ui/components/chat/TaskProgressIndicator";
import { TaskProgressBar } from "@liveagent/ui/components/chat/TaskProgressBar";
import { ToolApprovalBar } from "@liveagent/ui/components/chat/ToolApprovalBar";
import { useSequencedTaskProgress } from "@liveagent/ui/components/chat/useSequencedTaskProgress";
import { WorkspaceResourceSettingsDrawer } from "@liveagent/ui/components/chat/WorkspaceResourceSettingsDrawer";
import type {
GitCommitContextPayload,
Expand All @@ -18,13 +17,10 @@ import { RightDockPanel } from "@liveagent/ui/components/project-tools/RightDock
import { Button } from "@liveagent/ui/components/ui/button";
import { useConfirmDialog } from "@liveagent/ui/components/ui/confirm-dialog";
import { ScrollArea } from "@liveagent/ui/components/ui/scroll-area";
import { type Locale, LocaleContext, t as translate } from "@liveagent/ui/i18n/index";
import { LocaleContext, t as translate } from "@liveagent/ui/i18n/index";
import { normalizeLogicalLineEndings } from "@liveagent/ui/lib/chat/composerText";
import { openChatFileLink } from "@liveagent/ui/lib/chat/openChatFileLink";
import {
selectTodoProgressUpdates,
type TodoProgressUpdate,
} from "@liveagent/ui/lib/chat/taskProgress";
import { selectLatestTaskProgress } from "@liveagent/ui/lib/chat/taskProgress";
import {
readToolApprovalDeadlineAt,
readToolApprovalPending,
Expand Down Expand Up @@ -150,41 +146,6 @@ import type { SectionId } from "@/pages/settings/types";

const LOCAL_DRAFT_PREFIX = "__local_draft__:";

function CurrentTaskProgress(props: {
updates: readonly TodoProgressUpdate[];
isConversationRunning: boolean;
locale: Locale;
}) {
const { updates, isConversationRunning, locale } = props;
const snapshot = useSequencedTaskProgress(updates, isConversationRunning);
const labels = useMemo(() => {
if (!snapshot) return null;
return {
title: translate("chat.taskProgress.title", locale),
step: translate("chat.taskProgress.step", locale)
.replace("{current}", String(snapshot.currentStep))
.replace("{total}", String(snapshot.totalCount)),
completedCount: `${snapshot.completedCount}/${snapshot.totalCount} ${translate(
"chat.taskProgress.completedCount",
locale,
)}`,
running: translate("chat.taskProgress.running", locale),
pending: translate("chat.taskProgress.pending", locale),
paused: translate("chat.taskProgress.paused", locale),
completed: translate("chat.taskProgress.completed", locale),
};
}, [locale, snapshot]);

if (!snapshot || !labels) return null;
return (
<TaskProgressIndicator
snapshot={snapshot}
isConversationRunning={isConversationRunning}
labels={labels}
/>
);
}

function createLocalDraftConversationId() {
return `${LOCAL_DRAFT_PREFIX}${createUuid()}`;
}
Expand Down Expand Up @@ -4469,8 +4430,8 @@ export default function GatewayApp() {
return item?.title ?? "";
}, [selectedHistoryId, sidebarConversationsById]);
const transcriptRows = displayedTranscript.rows;
const taskProgressUpdates = useMemo(
() => selectTodoProgressUpdates(transcriptRows),
const taskProgressSnapshot = useMemo(
() => selectLatestTaskProgress(transcriptRows),
[transcriptRows],
);
// 当前会话的待审批工具:遍历渲染中的 transcript,筛出带 __toolApprovalPending 标记
Expand Down Expand Up @@ -5173,11 +5134,10 @@ export default function GatewayApp() {
onEditQueuedTurn={editQueuedTurn}
onRemoveQueuedTurn={removeQueuedTurn}
taskProgressBar={
<CurrentTaskProgress
<TaskProgressBar
key={displayedConversationId}
updates={taskProgressUpdates}
snapshot={taskProgressSnapshot}
isConversationRunning={transcriptBusy}
locale={settings.locale}
/>
}
approvalBar={approvalBar}
Expand Down
32 changes: 4 additions & 28 deletions crates/agent-gateway/web/src/i18n/config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { TASK_TRANSLATIONS } from "@liveagent/ui/i18n/taskTranslations";

/**
* Simple i18n translation layer
* Maps keys to localized strings for zh-CN and en-US
Expand All @@ -11,6 +13,7 @@ export const SUPPORTED_LOCALES = ["zh-CN", "en-US"] as const satisfies readonly

export const translations: Record<Locale, Record<string, string>> = {
"zh-CN": {
...TASK_TRANSLATIONS["zh-CN"],
/* ── App / Global ── */
"app.errorBoundaryCopy": "复制错误信息",
"app.errorBoundaryDesc": "界面渲染发生错误,正在进行的任务不受影响。请重新加载页面。",
Expand Down Expand Up @@ -283,22 +286,12 @@ export const translations: Record<Locale, Record<string, string>> = {
"chat.tool.running": "运行中",
"chat.tool.failed": "失败",
"chat.tool.success": "已完成",
"chat.tool.aborted": "已中止",
"chat.tool.waiting": "等待",
"chat.tool.command": "命令",
"chat.tool.args": "参数",
"chat.tool.return": "返回",
"chat.tool.error": "(错误)",
"chat.tool.viewReturn": "查看返回内容",
"chat.tool.todoTitle": "任务清单",
"chat.tool.todoEmpty": "暂无任务",
"chat.taskProgress.title": "任务进度",
"chat.taskProgress.step": "第 {current} / {total} 步",
"chat.taskProgress.running": "运行中",
"chat.taskProgress.pending": "待处理",
"chat.taskProgress.paused": "已暂停或中断",
"chat.taskProgress.completed": "全部完成",
"chat.taskProgress.completedCount": "已完成",
"chat.tool.askUserTitle": "向你提问",
"chat.askUser.preparing": "正在准备问题",
"chat.askUser.waiting": "等待你的选择",
Expand Down Expand Up @@ -1343,10 +1336,6 @@ export const translations: Record<Locale, Record<string, string>> = {
"settings.builtinTool.send_message.desc": "与子代理之间收发消息",
"settings.builtinTool.send_message.detail":
"在主对话与子代理之间传递消息,用于协调多代理协作。需要子代理运行时;仅在对话场景注册。",
"settings.builtinTool.todo_write.name": "任务清单",
"settings.builtinTool.todo_write.desc": "创建与更新当前会话的任务清单",
"settings.builtinTool.todo_write.detail":
"让模型在处理多步骤任务时列出任务清单并逐项推进状态,进度以清单卡片实时展示在对话中。清单仅保存在当前对话内,不跨对话保留;仅在对话场景注册。",
"settings.builtinTool.ask_user_question.name": "用户提问",
"settings.builtinTool.ask_user_question.desc": "以选项卡片向你提问并等待选择",
"settings.builtinTool.ask_user_question.detail":
Expand Down Expand Up @@ -2237,6 +2226,7 @@ export const translations: Record<Locale, Record<string, string>> = {
},

"en-US": {
...TASK_TRANSLATIONS["en-US"],
/* ── App / Global ── */
"app.errorBoundaryCopy": "Copy error details",
"app.errorBoundaryDesc":
Expand Down Expand Up @@ -2527,22 +2517,12 @@ export const translations: Record<Locale, Record<string, string>> = {
"chat.tool.running": "Running",
"chat.tool.failed": "Failed",
"chat.tool.success": "Completed",
"chat.tool.aborted": "Aborted",
"chat.tool.waiting": "Waiting",
"chat.tool.command": "Command",
"chat.tool.args": "Args",
"chat.tool.return": "Return",
"chat.tool.error": "(Error)",
"chat.tool.viewReturn": "View Return",
"chat.tool.todoTitle": "Task list",
"chat.tool.todoEmpty": "No tasks yet",
"chat.taskProgress.title": "Task progress",
"chat.taskProgress.step": "Step {current} of {total}",
"chat.taskProgress.running": "Running",
"chat.taskProgress.pending": "Pending",
"chat.taskProgress.paused": "Paused or interrupted",
"chat.taskProgress.completed": "All completed",
"chat.taskProgress.completedCount": "completed",
"chat.tool.askUserTitle": "Question for you",
"chat.askUser.preparing": "Preparing questions",
"chat.askUser.waiting": "Waiting for your choice",
Expand Down Expand Up @@ -3626,10 +3606,6 @@ export const translations: Record<Locale, Record<string, string>> = {
"settings.builtinTool.send_message.desc": "Exchange messages with subagents",
"settings.builtinTool.send_message.detail":
"Relays messages between the main conversation and subagents to coordinate multi-agent work. Requires the subagent runtime; chat sessions only.",
"settings.builtinTool.todo_write.name": "Task List",
"settings.builtinTool.todo_write.desc": "Create and update a task list for the current session",
"settings.builtinTool.todo_write.detail":
"Lets the model plan multi-step work as a task list and advance each item's status as it goes, shown as a live checklist card in the conversation. The list lives only in the current conversation and is not carried across conversations; chat sessions only.",
"settings.builtinTool.ask_user_question.name": "Ask User",
"settings.builtinTool.ask_user_question.desc":
"Ask you multiple-choice questions in a card and wait for your selections",
Expand Down
21 changes: 9 additions & 12 deletions crates/agent-gateway/web/src/lib/tools/builtinTypes.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import type { TaskListResultDetails } from "@liveagent/ui/contracts/task";
import type {
SubagentBatchDetails,
SubagentCardDetails,
SubagentMessageDetails,
} from "@liveagent/ui/lib/subagents/protocol";
import type { Tool, ToolCall, ToolResultMessage } from "../agentTypes";

export type {
TaskItem,
TaskListResultDetails,
TaskListState,
TaskStatus,
} from "@liveagent/ui/contracts/task";

export type BuiltinToolGroupId = "fs" | "shell" | "skill" | "system" | "mcp" | "subagent";

export type BuiltinToolDisplayCategory =
Expand Down Expand Up @@ -345,17 +353,6 @@ export type GrepResultDetails = {
files: GrepResultFileSummary[];
};

export type TodoItem = {
content: string;
status: "pending" | "in_progress" | "completed";
activeForm: string;
};

export type TodoWriteResultDetails = {
kind: "todo_write";
todos: TodoItem[];
};

export type BuiltinToolResultDetails =
| ReadTextResultDetails
| ReadImageResultDetails
Expand All @@ -374,5 +371,5 @@ export type BuiltinToolResultDetails =
| ListResultDetails
| GlobResultDetails
| GrepResultDetails
| TodoWriteResultDetails
| TaskListResultDetails
| Record<string, unknown>;
5 changes: 0 additions & 5 deletions crates/agent-gateway/web/src/pages/chat/AssistantBubble.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,6 @@ export const AssistantBubble = memo(function AssistantBubble(props: {
workdir,
onOpenFileLink,
} = props;
const isAborted = useMemo(
() => rounds.some((round) => round.meta?.stopReason === "aborted"),
[rounds],
);
// 回复末尾的已编辑文件卡:聚合整条回复所有 round 的 Write/Edit/Delete,
// 只在回复结束(流停止)后出现;脱敏视图(分享页隐藏工具内容)不渲染。
const changedFiles = useMemo(
Expand Down Expand Up @@ -87,7 +83,6 @@ export const AssistantBubble = memo(function AssistantBubble(props: {
thinkingOpen={round.thinkingOpen}
readOnly={readOnly}
redactToolContent={redactToolContent}
isAborted={isAborted}
workdir={workdir}
onOpenFileLink={onOpenFileLink}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { ThinkingActivity } from "@liveagent/ui/components/chat/ThinkingActivity
import { UsagePanel } from "@liveagent/ui/components/chat/UsagePanel";
import { Markdown } from "@liveagent/ui/components/Markdown";
import { useLocale } from "@liveagent/ui/i18n/index";
import { isTodoWriteToolBlock } from "@liveagent/ui/lib/chat/taskProgress";
import { isTaskToolBlock } from "@liveagent/ui/lib/chat/taskProgress";
import { memo, useMemo, useState } from "react";
import { ChevronRight, RefreshCw } from "../../../components/icons";
import type { ChatFileLink } from "../../../lib/chat/chatFileLinks";
Expand Down Expand Up @@ -90,7 +90,6 @@ export const RoundContent = memo(function RoundContent(props: {
renderMode?: "streaming" | "static";
readOnly?: boolean;
redactToolContent?: boolean;
isAborted?: boolean;
workdir?: string;
onOpenFileLink?: (link: ChatFileLink) => void;
}) {
Expand All @@ -108,13 +107,12 @@ export const RoundContent = memo(function RoundContent(props: {
renderMode,
readOnly = false,
redactToolContent = false,
isAborted = false,
workdir,
onOpenFileLink,
} = props;
const groupedBlocks = useMemo(() => groupRoundBlocks(round.blocks), [round.blocks]);
const visibleGroupedBlocks = useMemo(
() => groupedBlocks.filter((block) => !isTodoWriteToolBlock(block)),
() => groupedBlocks.filter((block) => !isTaskToolBlock(block)),
[groupedBlocks],
);
const hasContent =
Expand Down Expand Up @@ -159,20 +157,7 @@ export const RoundContent = memo(function RoundContent(props: {
if (!hasContent) return null;

return (
<div
className={
isLive
? "space-y-2"
: // Settled rounds freeze todo-card animations; the strike-through /
// dimming of incomplete items is reserved for aborted replies —
// a normally completed reply may legitimately leave todos open.
`space-y-2 [&_.todo-list-view_.animate-spin]:!animate-none [&_.todo-list-view_.shimmer]:!animate-none${
isAborted
? " [&_.todo-list-view_[data-todo-incomplete]>span:last-child]:!text-muted-foreground/40 [&_.todo-list-view_[data-todo-incomplete]>span:last-child]:line-through"
: ""
}`
}
>
<div className="space-y-2">
{isActive &&
isLive &&
normalizedToolStatus &&
Expand Down Expand Up @@ -229,7 +214,6 @@ export const RoundContent = memo(function RoundContent(props: {
<MemoToolCallItem
key={block.key}
item={block.item}
isAborted={isAborted}
isRunning={Boolean(
isLive &&
block.item.toolCall.id &&
Expand All @@ -246,7 +230,6 @@ export const RoundContent = memo(function RoundContent(props: {
<ToolTraceGroup
key={block.key}
items={block.items}
isAborted={isAborted}
runningToolCallIds={
isLive
? (runningToolCallIds ?? EMPTY_RUNNING_TOOL_CALL_IDS)
Expand Down
Loading
Loading