diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx
index e790aa80f..eb3f5d2a4 100644
--- a/src/renderer/App.tsx
+++ b/src/renderer/App.tsx
@@ -57,6 +57,7 @@ import {
useAgentExecution,
useAgentCapabilities,
useMergeTransferHandlers,
+ useForkConversation,
useSummarizeAndContinue,
// Git
useFileTreeManagement,
@@ -1082,6 +1083,14 @@ function MaestroConsoleInner() {
setActiveSessionId,
});
+ // Fork conversation hook - creates a new session from a point in conversation history
+ const handleForkConversation = useForkConversation(
+ sessions,
+ setSessions,
+ activeSessionId,
+ setActiveSessionId
+ );
+
// Summarize & Continue hook for context compaction (non-blocking, per-tab)
const {
summarizeState,
@@ -2277,6 +2286,7 @@ function MaestroConsoleInner() {
handleMainPanelInputBlur,
handleOpenPromptComposer,
handleReplayMessage,
+ handleForkConversation,
handleMainPanelFileClick,
handleNavigateBack: handleFileTabNavigateBack,
handleNavigateForward: handleFileTabNavigateForward,
diff --git a/src/renderer/components/MainPanel/MainPanel.tsx b/src/renderer/components/MainPanel/MainPanel.tsx
index 2142db5d9..a9eddfd7c 100644
--- a/src/renderer/components/MainPanel/MainPanel.tsx
+++ b/src/renderer/components/MainPanel/MainPanel.tsx
@@ -670,6 +670,7 @@ export const MainPanel = React.memo(
onInputBlur={props.onInputBlur}
onOpenPromptComposer={props.onOpenPromptComposer}
onReplayMessage={props.onReplayMessage}
+ onForkConversation={props.onForkConversation}
fileTree={props.fileTree}
onFileClick={props.onFileClick}
refreshFileTree={props.refreshFileTree}
diff --git a/src/renderer/components/MainPanel/MainPanelContent.tsx b/src/renderer/components/MainPanel/MainPanelContent.tsx
index 806d1dcca..236dcc38f 100644
--- a/src/renderer/components/MainPanel/MainPanelContent.tsx
+++ b/src/renderer/components/MainPanel/MainPanelContent.tsx
@@ -173,6 +173,7 @@ export interface MainPanelContentProps {
onInputBlur?: () => void;
onOpenPromptComposer?: () => void;
onReplayMessage?: (text: string, images?: string[]) => void;
+ onForkConversation?: (logId: string) => void;
fileTree?: FileNode[];
onFileClick?: (relativePath: string, options?: { openInNewTab?: boolean }) => void;
refreshFileTree?: (
@@ -327,6 +328,7 @@ export const MainPanelContent = React.memo(function MainPanelContent(props: Main
onInputBlur,
onOpenPromptComposer,
onReplayMessage,
+ onForkConversation,
fileTree,
onFileClick,
refreshFileTree,
@@ -547,6 +549,7 @@ export const MainPanelContent = React.memo(function MainPanelContent(props: Main
markdownEditMode={chatRawTextMode}
setMarkdownEditMode={useSettingsStore.getState().setChatRawTextMode}
onReplayMessage={onReplayMessage}
+ onForkConversation={onForkConversation}
fileTree={fileTree}
cwd={
activeSession.cwd?.startsWith(activeSession.fullPath)
diff --git a/src/renderer/components/MainPanel/types.ts b/src/renderer/components/MainPanel/types.ts
index 9511476dd..c7e58001b 100644
--- a/src/renderer/components/MainPanel/types.ts
+++ b/src/renderer/components/MainPanel/types.ts
@@ -196,6 +196,7 @@ export interface MainPanelProps {
onOpenPromptComposer?: () => void;
// Replay a user message (AI mode)
onReplayMessage?: (text: string, images?: string[]) => void;
+ onForkConversation?: (logId: string) => void;
// File tree for linking file references in AI responses
fileTree?: import('../../types/fileTree').FileNode[];
// Callback when a file link is clicked in AI response
diff --git a/src/renderer/components/TerminalOutput.tsx b/src/renderer/components/TerminalOutput.tsx
index 74c511c97..91185192f 100644
--- a/src/renderer/components/TerminalOutput.tsx
+++ b/src/renderer/components/TerminalOutput.tsx
@@ -13,6 +13,7 @@ import {
Save,
Share2,
Hammer,
+ GitFork,
} from 'lucide-react';
import type { Session, Theme, LogEntry, FocusArea, AgentError } from '../types';
import type { FileNode } from '../types/fileTree';
@@ -176,6 +177,8 @@ interface LogItemProps {
// Publish to GitHub Gist (AI mode only, non-user messages, requires gh CLI)
ghCliAvailable?: boolean;
onPublishGist?: (text: string) => void;
+ // Fork conversation from this message (AI mode only, user and ai source messages)
+ onForkConversation?: (logId: string) => void;
// Message alignment
userMessageAlignment: 'left' | 'right';
}
@@ -218,6 +221,7 @@ const LogItemComponent = memo(
onSaveToFile,
ghCliAvailable,
onPublishGist,
+ onForkConversation,
userMessageAlignment,
}: LogItemProps) => {
// Ref for the log item container - used for scroll-into-view on expand
@@ -913,6 +917,17 @@ const LogItemComponent = memo(
)}
+ {/* Fork conversation from this message - AI and user messages only */}
+ {(log.source === 'ai' || log.source === 'user') && isAIMode && onForkConversation && (
+
+ )}
{/* Publish to GitHub Gist - only for AI responses when gh CLI available */}
{log.source !== 'user' && isAIMode && ghCliAvailable && onPublishGist && (