fix: preserve replies after session recovery

This commit is contained in:
john
2026-07-27 19:59:09 +08:00
parent 17c59fde71
commit d1be5d9e2a
3 changed files with 87 additions and 0 deletions
+10
View File
@@ -4,6 +4,7 @@
*/
import { extractConversationMessageText } from './conversation-memory.mjs';
import { mergeConversationSnapshot } from './chat-finish-sync.mjs';
export function parseStoredConversationRow(row) {
if (!row) return null;
@@ -215,6 +216,15 @@ export function repairConversationFromDbRows(gooseMessages, dbRows) {
return Array.isArray(gooseMessages) ? gooseMessages : [];
}
if (dbBuilt.length === 0) return Array.isArray(gooseMessages) ? gooseMessages : [];
const goose = Array.isArray(gooseMessages) ? gooseMessages : [];
const dbIds = new Set(dbBuilt.map((message) => message?.id).filter(Boolean));
const hasSharedAnchor = goose.some((message) => message?.id && dbIds.has(message.id));
if (hasSharedAnchor) {
// A recovered Goose session can contain only the last DB-backed user turn
// plus replies created after rotation. Prefer DB content for shared history,
// while retaining every Goose-only reply and follow-up around that anchor.
return mergeConversationSnapshot(goose, dbBuilt);
}
return dbBuilt;
}
+64
View File
@@ -69,6 +69,70 @@ test('shouldRepairConversationFromDb when goose has empty placeholders', () => {
assert.match(repaired[1].content[0].text, /中考/);
});
test('repairConversationFromDbRows retains the fresh Goose tail after session recovery', () => {
const goose = [
gooseMsg('recovery-user', 'user', '【Memind 任务编排】继续修改页面'),
gooseMsg('fresh-assistant', 'assistant', '第一轮修改已完成'),
gooseMsg('followup-user', 'user', '再加一个页脚'),
gooseMsg('followup-assistant', 'assistant', '页脚也已完成'),
];
const dbRows = [
{
message_key: 'old-user',
role: 'user',
text: '写一首诗并做成页面',
created_at: 1,
sequence_no: 0,
},
{
message_key: 'old-assistant',
role: 'assistant',
text: '初版页面已完成',
created_at: 2,
sequence_no: 1,
},
{
message_key: 'old-followup-user',
role: 'user',
text: '把标题改成金色',
created_at: 3,
sequence_no: 2,
},
{
message_key: 'old-followup-assistant',
role: 'assistant',
text: '标题已改成金色',
created_at: 4,
sequence_no: 3,
},
{
message_key: 'recovery-user',
role: 'user',
text: '继续修改页面',
created_at: 5,
sequence_no: 4,
},
];
const repaired = repairConversationFromDbRows(goose, dbRows);
assert.deepEqual(
repaired.map((message) => message.id),
[
'old-user',
'old-assistant',
'old-followup-user',
'old-followup-assistant',
'recovery-user',
'fresh-assistant',
'followup-user',
'followup-assistant',
],
);
assert.equal(repaired[4].content[0].text, '继续修改页面');
assert.equal(repaired[7].content[0].text, '页脚也已完成');
});
test('shouldRepairConversationFromDb returns false when goose is complete', () => {
const goose = [
gooseMsg('u1', 'user', 'hello'),
@@ -211,6 +211,19 @@ runtime 也必须和发布 manifest 的 git commit 对齐。
- 源码静态检查:`npm run verify:chat-finish-sync`
- 文档:本文件
### 恢复会话快照:禁止用旧 DB 历史覆盖新回复
视觉上下文不兼容等故障会把 Agent Run 旋转到新 Goose session。新 session
可能只包含最后一条数据库已同步的用户消息,以及旋转后新生成的助手回复和后续追问;
此时数据库旧历史条数通常更多。
- `conversation-repair.mjs` 发现 Goose 与 DB 存在共同消息 ID 时,必须以该 ID
为锚点合并:共享历史采用 DB 内容,同时保留 Goose 独有的新回复、后续追问和修改结果
- 只有双方没有共同消息 ID,且 Goose 确实是空占位/残缺记录时,才允许用 DB 历史整体修复
- Portal 保存 session snapshot 前不得把“当前同步计数”与“旧 messages_json”组合成伪最新快照
守卫:`conversation-repair.test.mjs` 中的 session recovery fresh-tail 用例。
---
## 3. 页面需求澄清:禁止误报“未生成 public HTML”