feat(conversation): replicate in-place user message editing (TODO 03)

This commit is contained in:
xiaohan.yuan committed 2026-10-17 06:31:37 +08:00
1 parent ed31a7c1b4
commit ee51208931
18 files changed
+992 -67

No files matched your search

+9 -2
View File
@@ -29,7 +29,7 @@ import { createInteractionBroker, respondToInteraction } from './interaction-bro
import { attachmentPrompt, commitAttachments, getAttachment, removeAttachment, stageAttachment, stageAttachmentPath } from './attachments'
import { transcribeAudio } from './speech'
import { getRevertChanges } from '@shared/revert'
import { restoreWorkspaceFiles } from './workspace-revert'
import { restoreWorkspaceFiles, rollbackEditedArtifacts } from './workspace-revert'
import { assistantWireHistory } from './agent/history'
import * as models from './models'
import * as workspaces from './workspaces'
@@ -563,7 +563,10 @@ export function registerIpc(getWindow: () => BrowserWindow | null): void {
}
const target = history[targetIndex]
if (!target || target.role !== 'user') throw new Error('未找到对应用户消息')
if (kernel) {
// 原版编辑语义(userMessage.edit.hint):产物回退至此消息前版本,再从此处重新开始对话。
const editUndo = payload.action === 'edit' ? await rollbackEditedArtifacts(workspaceRoot, history, payload.fromMessageId!) : undefined
try {
if (kernel) {
if (target.nativeSessionPath && target.nativeSessionPath !== ompSessionPath(sessionId)) throw new Error('这条历史消息没有当前 OMP 会话的分支记录')
let entryId = target.nativeEntryId
if (!entryId) {
@@ -588,6 +591,10 @@ export function registerIpc(getWindow: () => BrowserWindow | null): void {
}
const entry = ompKernels.get(sessionId)
if (entry && entryId) entry.appliedBranchId = entryId
}
} catch (error) {
if (editUndo) await editUndo().catch(() => undefined)
throw error
}
history = history.slice(0, targetIndex)
}
+13
View File
@@ -2,6 +2,8 @@ import { promises as fs } from 'node:fs'
import { dirname, isAbsolute, relative, resolve, sep, join } from 'node:path'
import { randomUUID } from 'node:crypto'
import type { RevertChange } from '@shared/revert'
import { getRevertChanges } from '@shared/revert'
import type { ChatMessage } from '@shared/types'
async function replaceFile(path: string, content: string | null): Promise<void> {
if (content === null) { await fs.unlink(path); return }
@@ -33,3 +35,14 @@ export async function restoreWorkspaceFiles(workspace: string, changes: RevertCh
} catch (error) { await undo(); throw error }
return undo
}
/**
* 编辑提交前的产物回退(原版 userMessage.edit.hint:产物回退至此消息前版本)。
* 返回撤销函数;该消息之后没有可回退的写入时返回 undefined。
* 记录不完整时由 getRevertChanges 抛错,调用方应中止提交而不是静默保留文件修改。
*/
export async function rollbackEditedArtifacts(workspace: string, messages: ChatMessage[], userMessageId: string): Promise<(() => Promise<void>) | undefined> {
const changes = getRevertChanges(messages, userMessageId)
if (!changes.length) return undefined
return restoreWorkspaceFiles(workspace, changes)
}