diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx
index 9ef221e262..b6751bcb2d 100644
--- a/apps/web/src/components/ChatView.tsx
+++ b/apps/web/src/components/ChatView.tsx
@@ -134,6 +134,7 @@ import {
} from "../composerDraftStore";
import {
appendTerminalContextsToPrompt,
+ deriveDisplayedUserMessageState,
formatTerminalContextLabel,
type TerminalContextDraft,
type TerminalContextSelection,
@@ -3490,6 +3491,29 @@ export default function ChatView(props: ChatViewProps) {
void onRevertToTurnCountRef.current(targetTurnCount);
}, []);
+ const onEditUserMessage = useCallback(
+ async (messageId: MessageId, text: string) => {
+ const targetTurnCount = revertTurnCountRef.current.get(messageId);
+ if (typeof targetTurnCount !== "number") {
+ return;
+ }
+
+ const displayedMessage = deriveDisplayedUserMessageState(text);
+ const draftText = displayedMessage.visibleText || text;
+ clearComposerDraftContent(composerDraftTarget);
+ setComposerDraftPrompt(composerDraftTarget, draftText);
+ promptRef.current = draftText;
+ composerRef.current?.resetCursorState({
+ cursor: draftText.length,
+ prompt: draftText,
+ detectTrigger: true,
+ });
+ composerRef.current?.focusAtEnd();
+ await onRevertToTurnCountRef.current(targetTurnCount);
+ },
+ [clearComposerDraftContent, composerDraftTarget, composerRef, setComposerDraftPrompt],
+ );
+
// Empty state: no active thread
if (!activeThread) {
return ;
@@ -3568,6 +3592,7 @@ export default function ChatView(props: ChatViewProps) {
onOpenTurnDiff={onOpenTurnDiff}
revertTurnCountByUserMessageId={revertTurnCountByUserMessageId}
onRevertUserMessage={onRevertUserMessage}
+ onEditUserMessage={onEditUserMessage}
isRevertingCheckpoint={isRevertingCheckpoint}
onImageExpand={onExpandTimelineImage}
markdownCwd={gitCwd ?? undefined}
diff --git a/apps/web/src/components/chat/MessagesTimeline.browser.tsx b/apps/web/src/components/chat/MessagesTimeline.browser.tsx
index 0eb5c8a1fc..efd52aa604 100644
--- a/apps/web/src/components/chat/MessagesTimeline.browser.tsx
+++ b/apps/web/src/components/chat/MessagesTimeline.browser.tsx
@@ -62,6 +62,7 @@ function buildProps() {
onOpenTurnDiff: vi.fn(),
revertTurnCountByUserMessageId: new Map(),
onRevertUserMessage: vi.fn(),
+ onEditUserMessage: vi.fn(),
isRevertingCheckpoint: false,
onImageExpand: vi.fn(),
activeThreadEnvironmentId: EnvironmentId.make("environment-local"),
diff --git a/apps/web/src/components/chat/MessagesTimeline.test.tsx b/apps/web/src/components/chat/MessagesTimeline.test.tsx
index a8a53831a2..45c3f6cf12 100644
--- a/apps/web/src/components/chat/MessagesTimeline.test.tsx
+++ b/apps/web/src/components/chat/MessagesTimeline.test.tsx
@@ -88,6 +88,7 @@ function buildProps() {
onOpenTurnDiff: () => {},
revertTurnCountByUserMessageId: new Map(),
onRevertUserMessage: () => {},
+ onEditUserMessage: () => {},
isRevertingCheckpoint: false,
onImageExpand: () => {},
activeThreadEnvironmentId: ACTIVE_THREAD_ENVIRONMENT_ID,
diff --git a/apps/web/src/components/chat/MessagesTimeline.tsx b/apps/web/src/components/chat/MessagesTimeline.tsx
index e4b683592e..314ebdd3b4 100644
--- a/apps/web/src/components/chat/MessagesTimeline.tsx
+++ b/apps/web/src/components/chat/MessagesTimeline.tsx
@@ -82,6 +82,7 @@ interface TimelineRowSharedState {
workspaceRoot: string | undefined;
activeThreadEnvironmentId: EnvironmentId;
onRevertUserMessage: (messageId: MessageId) => void;
+ onEditUserMessage: (messageId: MessageId, text: string) => void;
onImageExpand: (preview: ExpandedImagePreview) => void;
onOpenTurnDiff: (turnId: TurnId, filePath?: string) => void;
}
@@ -106,6 +107,7 @@ interface MessagesTimelineProps {
onOpenTurnDiff: (turnId: TurnId, filePath?: string) => void;
revertTurnCountByUserMessageId: Map;
onRevertUserMessage: (messageId: MessageId) => void;
+ onEditUserMessage: (messageId: MessageId, text: string) => void;
isRevertingCheckpoint: boolean;
onImageExpand: (preview: ExpandedImagePreview) => void;
activeThreadEnvironmentId: EnvironmentId;
@@ -134,6 +136,7 @@ export const MessagesTimeline = memo(function MessagesTimeline({
onOpenTurnDiff,
revertTurnCountByUserMessageId,
onRevertUserMessage,
+ onEditUserMessage,
isRevertingCheckpoint,
onImageExpand,
activeThreadEnvironmentId,
@@ -205,6 +208,7 @@ export const MessagesTimeline = memo(function MessagesTimeline({
workspaceRoot,
activeThreadEnvironmentId,
onRevertUserMessage,
+ onEditUserMessage,
onImageExpand,
onOpenTurnDiff,
}),
@@ -221,6 +225,7 @@ export const MessagesTimeline = memo(function MessagesTimeline({
workspaceRoot,
activeThreadEnvironmentId,
onRevertUserMessage,
+ onEditUserMessage,
onImageExpand,
onOpenTurnDiff,
],
@@ -355,16 +360,28 @@ function TimelineRowContent({ row }: { row: TimelineRow }) {
)}
{canRevertAgentWork && (
-
+ <>
+
+
+ >
)}