feat: add dialog for "Start new session from this answer" (#1501)

* feat: add dialog for "Start new session from this answer"

Replace the one-click fork action on assistant messages with a dialog
(ForkSessionDialog) that lets the user pick model, thinking level, and
agent, plus edit the instructions sent to the new session.

The instructions field is prefilled with the previous fixed fork prompt
and is mandatory. The composed message is now fully visible (no synthetic
preface): the user's instructions sit above a short fixed connective that
opens the assistant content. createSessionFromAssistantMessage takes the
chosen execution params instead of reading from config.

Also fix TodoSendDialog visuals: narrower vertical layout, model trigger
no longer stretches with centered text, and the agent/thinking dropdowns
portal to body so opening them no longer nudges the dialog height. Extract
the shared ThinkingPill into its own component.

* fix: address review feedback on fork session dialog

- Fix "bellow" -> "below" typo in the fork content preface (now user-visible
  since the message is no longer synthetic)
- Reset ForkSessionDialog state only on open transition, reading the config
  store snapshot via getState() so background store refreshes can't discard
  in-progress instruction edits
This commit is contained in:
Bohdan Triapitsyn
2026-06-02 12:53:30 +03:00
committed by GitHub
parent aefbdbc52f
commit 6a88cd09cc
17 changed files with 334 additions and 74 deletions
+9 -9
View File
@@ -27,7 +27,7 @@ import { useCommandsStore } from "@/stores/useCommandsStore"
import { getSafeStorage } from "@/stores/utils/safeStorage"
import { markPendingUserSendAnimation } from "@/lib/userSendAnimation"
import { flattenAssistantTextParts } from "@/lib/messages/messageText"
import { EXECUTION_FORK_META_TEXT } from "@/lib/messages/executionMeta"
import { composeForkSessionMessage } from "@/lib/messages/executionMeta"
import { waitForWorktreeBootstrap } from "@/lib/worktrees/worktreeBootstrap"
import { waitForPendingDraftWorktreeRequest } from "@/lib/worktrees/pendingDraftWorktree"
import { resolveProjectForSessionDirectory } from "@/lib/projectResolution"
@@ -269,7 +269,7 @@ export type SessionUIState = {
forkFromMessage: (sessionId: string, messageId: string) => Promise<void>
handleSlashUndo: (sessionId: string) => Promise<void>
handleSlashRedo: (sessionId: string, options?: { fullUnrevert?: boolean }) => Promise<void>
createSessionFromAssistantMessage: (sourceMessageId: string) => Promise<void>
createSessionFromAssistantMessage: (sourceMessageId: string, execution: { providerID: string; modelID: string; variant: string; agent: string; instructions: string }) => Promise<void>
// Data access helpers (read from sync)
getSessionsByDirectory: (directory: string) => Session[]
@@ -1167,8 +1167,9 @@ export const useSessionUIStore = create<SessionUIState>()((set, get) => ({
// ---------------------------------------------------------------------------
// createSessionFromAssistantMessage — reads from sync
// ---------------------------------------------------------------------------
createSessionFromAssistantMessage: async (sourceMessageId) => {
createSessionFromAssistantMessage: async (sourceMessageId, execution) => {
if (!sourceMessageId) return
if (!execution?.instructions?.trim()) return
// Find which session this message belongs to by scanning sync state
const state = getDirectoryState()
@@ -1200,9 +1201,8 @@ export const useSessionUIStore = create<SessionUIState>()((set, get) => ({
const session = await get().createSession(undefined, directory ?? null, null)
if (!session) return
const { currentProviderId, currentModelId, currentAgentName } = useConfigStore.getState()
const pID = currentProviderId || useSelectionStore.getState().lastUsedProvider?.providerID
const mID = currentModelId || useSelectionStore.getState().lastUsedProvider?.modelID
const pID = execution.providerID || useSelectionStore.getState().lastUsedProvider?.providerID
const mID = execution.modelID || useSelectionStore.getState().lastUsedProvider?.modelID
if (!pID || !mID) return
@@ -1211,9 +1211,9 @@ export const useSessionUIStore = create<SessionUIState>()((set, get) => ({
id: session.id,
providerID: pID,
modelID: mID,
text: assistantPlanText,
prefaceText: EXECUTION_FORK_META_TEXT,
agent: currentAgentName ?? undefined,
variant: execution.variant || undefined,
text: composeForkSessionMessage(execution.instructions, assistantPlanText),
agent: execution.agent || undefined,
directory: sessionDirectory,
})
},