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
11 changes: 11 additions & 0 deletions kagent-ui/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# CHANGELOG

## 2026-04-16

- 修复聊天界面 assistant 回复重复显示:
- 为 assistant UI 消息补充 `turnId` 元信息,并在前端状态层归并同一 turn 中相邻的 assistant 气泡,避免 `item/started`、`item/agentMessage/delta`、`item/completed` 之间的消息 ID 不一致时留下重复回复。
- `thread/resume` 历史恢复沿用同样的相邻 assistant 归并规则,避免切换或重开线程后再次出现重复欢迎语。
- 新增 `messages.test.ts` 回归测试,覆盖相邻 assistant 合并、chunked assistant 片段重组、live delta 归并与历史恢复路径,并确保中间夹有其他 item 时不会误合并。
- 修复聊天界面 SSE 增量消息累积问题:
- `use-codex-session` 改为为 `reasoning` 与 `mcpToolCall/progress` 使用按 turn 稳定的临时消息 ID,流式增量改为原地更新,不再为每个增量块堆出新的消息条目。
- `turn/completed` 与 `error` 事件现在会清理当前 turn 的临时思考/工具进度卡片,避免对话结束后残留重复占位消息。
- 新增 `messages.test.ts`,覆盖临时消息清理与流式占位原地更新的纯函数行为。

## 2026-02-24

- 修复“查看沙箱日志”鉴权失败(401):
Expand Down
Binary file modified kagent-ui/bun.lockb
Binary file not shown.
25 changes: 21 additions & 4 deletions kagent-ui/components/assistant-ui/thread.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ import {
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { Button } from "@/components/ui/button";
import { getProviderTypeMeta, PROVIDER_TYPE_ICON_MAP } from "@/lib/model-provider-type";
import type { FileUpdateChange } from "@/lib/codex-app-server/v2/FileUpdateChange";
import {
getProviderTypeMeta,
PROVIDER_TYPE_ICON_MAP,
} from "@/lib/model-provider-type";
import {
Select,
SelectContent,
Expand All @@ -28,7 +32,7 @@ export type UserAttachment = { type: "image" | "localImage"; src: string };

export type UiMessage =
| { id: string; kind: "user"; text: string; attachments: UserAttachment[] }
| { id: string; kind: "assistant"; text: string }
| { id: string; kind: "assistant"; text: string; turnId?: string }
| { id: string; kind: "turnCompleted"; turnId: string; text: string }
| { id: string; kind: "reasoning"; summary: string[]; content: string[] }
| {
Expand All @@ -45,7 +49,7 @@ export type UiMessage =
id: string;
kind: "fileChange";
status: string;
changes: Array<{ path: string; kind: string; diff: string }>;
changes: Array<FileUpdateChange>;
output: string | null;
}
| {
Expand Down Expand Up @@ -469,7 +473,7 @@ function renderAssistantPayload(msg: Exclude<UiMessage, { kind: "user" }>) {
className="rounded-md border bg-background p-2"
>
<div className="text-xs text-muted-foreground">
{change.path} · {change.kind}
{change.path} · {formatFileChangeKind(change.kind)}
</div>
{change.diff ? (
<pre className="mt-2 max-h-80 overflow-auto text-xs">
Expand Down Expand Up @@ -646,6 +650,19 @@ function formatDuration(ms: number) {
return `${minutes}m ${rest.toFixed(0)}s`;
}

function formatFileChangeKind(kind: FileUpdateChange["kind"]) {
switch (kind.type) {
case "add":
return "add";
case "delete":
return "delete";
case "update":
return kind.move_path ? `update -> ${kind.move_path}` : "update";
}
const exhaustiveCheck: never = kind;
return exhaustiveCheck;
}

const markdownComponents: Components = {
h1: ({ children }) => (
<h1 className="mb-4 text-2xl font-bold tracking-tight">{children}</h1>
Expand Down
Loading
Loading