boardx / boardx/workspacex

聊天面板:任务结束时用户气泡与"确认理解"卡片重复出现(刷新后自愈)

Open Beginner friendly
#3,450 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
TypeScript
Stars
0
Forks
0
Avg merge
1h 7m
Merged PRs (30d)
969

Description

## 现象

用户直接报告(附截图):一个"确认一下我的理解,再开始"的 HITL 确认任务跑完之后,
**不刷新页面**的情况下,会话里出现:
1. 用户的原始提问气泡**重复**了一次;
2. 已经裁决过的"确认一下我的理解,再开始"卡片作为**多余的 UI** 残留在消息流中。

刷新页面后(重新挂载 hydration)两个问题都自愈——只剩一条用户提问和最终带 PDF 附件的回复。

## 根因

`apps/web/components/chat/copilotkit-v2-panel-body.tsx` 的两处"把服务端已落库消息
与内存里 `agent.messages` 合并"的去重逻辑(挂载 hydration 的 `live.length > 0` 分支,
以及 `handleRunRestored`)都只按消息 `id` 去重。

但乐观插入的用户消息(`send()`)在 `agent.messages` 里用的 id 是前端生成的
`clientMessageId`;这条消息落库后,`chat_messages` 的真实主键是服务端另外
`randomUUID()` 出来的一个**不同的 id**(`message-roundtrip.ts`)。`client_message_id`
只作为幂等去重的独立列存在,从未在 `listMessages`/`readAllPersistedMessages` 里投影
给前端。

于是:HITL 裁决("继续")之后 run 收尾时触发的 `handleRunRestored`(不需要刷新页面
就会跑)用"服务端真实 id"去重,认不出这就是刚才那条乐观插入的用户消息,把它整条
再插一遍 —— 这段消息里同时挂着 CopilotKit 内联渲染的 `confirm_task_intent` 中断卡片,
于是这条已经决裁过的确认卡片也跟着被渲染了第二次。

## 修复

- `apps/web/lib/copilotkit-v2-persisted-messages.ts`:`PersistedMessage` 新增
`clientMessageId` 投影(契约里 `DurableMessage.clientMessageId` 早就有,只是没读出来)。
- `apps/web/components/chat/copilotkit-v2-panel-body.tsx`:上述两处去重改为同时比对
`id` 与 `clientMessageId`,命中任一个都算"已经在场",不再重复插入。

验证:`pnpm typecheck` / `pnpm lint`(改动文件)/ `pnpm vitest run` 相关用例(31 个测试
文件、223 个测试)全绿。

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with apps/web/lib/copilotkit-v2-persisted-messages.ts to trace how persisted messages are projected, then inspect the two merge paths in apps/web/components/chat/copilotkit-v2-panel-body.tsx. Run pnpm typecheck, pnpm lint on the changed files, and the related pnpm vitest run covering 31 files and 223 tests. Done means the post-HITL flow shows one user message and no duplicate confirmation card without a refresh.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
frontend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
88/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.