fix(git): materialize draft session for generate (#1761)
* fix(git): materialize draft session for generate * fix(sync): remove redundant draft session side effects --------- Co-authored-by: Leonid Skorobogatyy <bash@opencode.itc.local>
This commit is contained in:
committed by
GitHub
co-authored by
Leonid Skorobogatyy
parent
b0e476ab7c
commit
03e6f789a4
@@ -2,8 +2,8 @@
|
||||
import * as gitHttp from './gitApiHttp';
|
||||
import { opencodeClient } from './opencode/client';
|
||||
import { renderMagicPrompt } from './magicPrompts';
|
||||
import { useSessionUIStore } from '@/sync/session-ui-store';
|
||||
import { useContextStore } from '@/stores/contextStore';
|
||||
import { materializeOpenDraftSession, useSessionUIStore } from '@/sync/session-ui-store';
|
||||
import { useSelectionStore } from '@/sync/selection-store';
|
||||
import { useConfigStore } from '@/stores/useConfigStore';
|
||||
import { getRegisteredRuntimeAPIs } from '@/contexts/runtimeAPIRegistry';
|
||||
|
||||
@@ -217,11 +217,7 @@ export async function generateCommitMessage(
|
||||
): Promise<{ message: import('./api/types').GeneratedCommitMessage }> {
|
||||
const startedAt = Date.now();
|
||||
void options;
|
||||
const generationSession = resolveSessionGenerationContext();
|
||||
|
||||
if (!generationSession) {
|
||||
throw new Error('Select existing session for generation');
|
||||
}
|
||||
const generationSession = await resolveGenerationSessionContext();
|
||||
|
||||
console.info('[git-generation][browser] request', {
|
||||
transport: 'session',
|
||||
@@ -283,10 +279,7 @@ export async function generatePullRequestDescription(
|
||||
payload: { base: string; head: string; context?: string; zenModel?: string; providerId?: string; modelId?: string }
|
||||
): Promise<import('./api/types').GeneratedPullRequestDescription> {
|
||||
const startedAt = Date.now();
|
||||
const generationSession = resolveSessionGenerationContext();
|
||||
if (!generationSession) {
|
||||
throw new Error('Select existing session for generation');
|
||||
}
|
||||
const generationSession = await resolveGenerationSessionContext();
|
||||
|
||||
const commitLog = await getGitLog(directory, {
|
||||
from: payload.base,
|
||||
@@ -387,19 +380,65 @@ type SessionGenerationContext = {
|
||||
variant?: string;
|
||||
};
|
||||
|
||||
const GENERATION_CONFIG_ERROR = 'No default provider or model configured. Please select a provider and model in settings first.';
|
||||
|
||||
async function resolveGenerationSessionContext(): Promise<SessionGenerationContext> {
|
||||
const activeSession = resolveSessionGenerationContext();
|
||||
if (activeSession) {
|
||||
return activeSession;
|
||||
}
|
||||
|
||||
const draft = useSessionUIStore.getState().newSessionDraft;
|
||||
if (!draft?.open) {
|
||||
throw new Error('Select existing session for generation');
|
||||
}
|
||||
|
||||
const config = useConfigStore.getState();
|
||||
if (!config.currentProviderId || !config.currentModelId) {
|
||||
throw new Error(GENERATION_CONFIG_ERROR);
|
||||
}
|
||||
|
||||
const createdDraftSession = await materializeOpenDraftSession({
|
||||
providerID: config.currentProviderId,
|
||||
modelID: config.currentModelId,
|
||||
agent: config.currentAgentName || undefined,
|
||||
variant: config.currentVariant || undefined,
|
||||
});
|
||||
|
||||
if (!createdDraftSession) {
|
||||
const retry = resolveSessionGenerationContext();
|
||||
if (retry) {
|
||||
return retry;
|
||||
}
|
||||
throw new Error('Failed to create session for generation');
|
||||
}
|
||||
|
||||
return {
|
||||
sessionId: createdDraftSession.sessionId,
|
||||
providerID: config.currentProviderId,
|
||||
modelID: config.currentModelId,
|
||||
agent: createdDraftSession.agent,
|
||||
variant: config.currentVariant || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
const resolveSessionGenerationContext = (): SessionGenerationContext | null => {
|
||||
const sessionId = useSessionUIStore.getState().currentSessionId;
|
||||
if (!sessionId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const context = useContextStore.getState();
|
||||
const selection = useSelectionStore.getState();
|
||||
const config = useConfigStore.getState();
|
||||
const lastChoice = useSessionUIStore.getState().getLastUserChoice(sessionId);
|
||||
|
||||
const agent = context.getSessionAgentSelection(sessionId) || config.currentAgentName || undefined;
|
||||
const sessionModel = context.getSessionModelSelection(sessionId);
|
||||
const agentModel = agent ? context.getAgentModelForSession(sessionId, agent) : null;
|
||||
const selectedModel = agentModel || sessionModel || (config.currentProviderId && config.currentModelId
|
||||
const agent = selection.getSessionAgentSelection(sessionId) || lastChoice?.agent || config.currentAgentName || undefined;
|
||||
const sessionModel = selection.getSessionModelSelection(sessionId);
|
||||
const agentModel = agent ? selection.getAgentModelForSession(sessionId, agent) : null;
|
||||
const lastChoiceModel = lastChoice?.providerID && lastChoice.modelID
|
||||
? { providerId: lastChoice.providerID, modelId: lastChoice.modelID }
|
||||
: null;
|
||||
const selectedModel = agentModel || sessionModel || lastChoiceModel || (config.currentProviderId && config.currentModelId
|
||||
? { providerId: config.currentProviderId, modelId: config.currentModelId }
|
||||
: null);
|
||||
|
||||
@@ -407,10 +446,18 @@ const resolveSessionGenerationContext = (): SessionGenerationContext | null => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const agentVariant = agent
|
||||
? context.getAgentModelVariantForSession(sessionId, agent, selectedModel.providerId, selectedModel.modelId)
|
||||
const selectionVariant = agent
|
||||
? selection.getAgentModelVariantForSession(sessionId, agent, selectedModel.providerId, selectedModel.modelId)
|
||||
: undefined;
|
||||
const variant = agentVariant || config.currentVariant || undefined;
|
||||
const lastChoiceVariant = lastChoiceModel
|
||||
&& lastChoiceModel.providerId === selectedModel.providerId
|
||||
&& lastChoiceModel.modelId === selectedModel.modelId
|
||||
? lastChoice?.variant
|
||||
: undefined;
|
||||
const configVariant = config.currentProviderId === selectedModel.providerId && config.currentModelId === selectedModel.modelId
|
||||
? config.currentVariant
|
||||
: undefined;
|
||||
const variant = selectionVariant || lastChoiceVariant || configVariant || undefined;
|
||||
|
||||
return {
|
||||
sessionId,
|
||||
|
||||
@@ -30,6 +30,7 @@ import { markPendingUserSendAnimation } from "@/lib/userSendAnimation"
|
||||
import { flattenAssistantTextParts } from "@/lib/messages/messageText"
|
||||
import { composeForkSessionMessage } from "@/lib/messages/executionMeta"
|
||||
import { waitForPendingDraftWorktreeRequest } from "@/lib/worktrees/pendingDraftWorktree"
|
||||
import { waitForWorktreeBootstrap } from "@/lib/worktrees/worktreeBootstrap"
|
||||
import { resolveProjectForSessionDirectory } from "@/lib/projectResolution"
|
||||
import {
|
||||
getSyncSessions,
|
||||
@@ -408,6 +409,75 @@ const writeRuntimeSessionMemory = (key: string, patch: Partial<RuntimeSessionMem
|
||||
})
|
||||
}
|
||||
|
||||
type MaterializedDraftSession = {
|
||||
sessionId: string
|
||||
directory: string | null
|
||||
agent?: string
|
||||
syntheticParts?: SyntheticContextPart[]
|
||||
}
|
||||
|
||||
export async function materializeOpenDraftSession(selection: {
|
||||
providerID: string
|
||||
modelID: string
|
||||
agent?: string
|
||||
variant?: string
|
||||
}): Promise<MaterializedDraftSession | null> {
|
||||
const store = useSessionUIStore.getState()
|
||||
const draft = store.newSessionDraft
|
||||
if (!draft?.open) return null
|
||||
|
||||
const trimmedAgent = typeof selection.agent === "string" && selection.agent.trim().length > 0
|
||||
? selection.agent.trim()
|
||||
: undefined
|
||||
let draftDirectoryOverride = draft.bootstrapPendingDirectory ?? draft.directoryOverride ?? null
|
||||
const draftProjectId = draft.selectedProjectId ?? null
|
||||
|
||||
if (draft.pendingWorktreeRequestId) {
|
||||
draftDirectoryOverride = await waitForPendingDraftWorktreeRequest(draft.pendingWorktreeRequestId)
|
||||
store.resolvePendingDraftWorktreeTarget(draft.pendingWorktreeRequestId, draftDirectoryOverride)
|
||||
}
|
||||
|
||||
if (draftDirectoryOverride) {
|
||||
await waitForWorktreeBootstrap(draftDirectoryOverride)
|
||||
}
|
||||
|
||||
const created = await store.createSession(draft.title, draftDirectoryOverride, draft.parentID ?? null)
|
||||
if (!created?.id) throw new Error("Failed to create session")
|
||||
|
||||
persistDraftTarget({
|
||||
projectId: draftProjectId,
|
||||
directory: normalizePath(draftDirectoryOverride ?? created.directory ?? null),
|
||||
})
|
||||
|
||||
const draftSyntheticParts = draft.syntheticParts
|
||||
const createdDirectory = normalizePath(draftDirectoryOverride ?? created.directory ?? null)
|
||||
const configState = useConfigStore.getState()
|
||||
void activateConfigForDirectory(createdDirectory).catch((error) => {
|
||||
console.warn("Failed to activate directory after creating session:", error)
|
||||
})
|
||||
|
||||
const effectiveDraftAgent = trimmedAgent ?? configState.currentAgentName
|
||||
|
||||
useSelectionStore.getState().saveSessionModelSelection(created.id, selection.providerID, selection.modelID)
|
||||
|
||||
if (effectiveDraftAgent) {
|
||||
useSelectionStore.getState().saveSessionAgentSelection(created.id, effectiveDraftAgent)
|
||||
useSelectionStore.getState().saveAgentModelForSession(created.id, effectiveDraftAgent, selection.providerID, selection.modelID)
|
||||
useSelectionStore.getState().saveAgentModelVariantForSession(created.id, effectiveDraftAgent, selection.providerID, selection.modelID, selection.variant)
|
||||
}
|
||||
|
||||
store.initializeNewOpenChamberSession(created.id, configState.agents ?? [])
|
||||
|
||||
store.setCurrentSession(created.id, createdDirectory)
|
||||
|
||||
return {
|
||||
sessionId: created.id,
|
||||
directory: createdDirectory,
|
||||
agent: effectiveDraftAgent,
|
||||
syntheticParts: draftSyntheticParts,
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Store
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -919,59 +989,21 @@ export const useSessionUIStore = create<SessionUIState>()((set, get) => ({
|
||||
|
||||
// ---- New session from draft ----
|
||||
if (!options?.sessionId && draft?.open) {
|
||||
const draftTargetFolderId = draft.targetFolderId
|
||||
let draftDirectoryOverride = draft.bootstrapPendingDirectory ?? draft.directoryOverride ?? null
|
||||
const draftProjectId = draft.selectedProjectId ?? null
|
||||
|
||||
if (draft.pendingWorktreeRequestId) {
|
||||
draftDirectoryOverride = await waitForPendingDraftWorktreeRequest(draft.pendingWorktreeRequestId)
|
||||
get().resolvePendingDraftWorktreeTarget(draft.pendingWorktreeRequestId, draftDirectoryOverride)
|
||||
}
|
||||
|
||||
const created = await get().createSession(draft.title, draftDirectoryOverride, draft.parentID ?? null)
|
||||
if (!created?.id) throw new Error("Failed to create session")
|
||||
|
||||
persistDraftTarget({
|
||||
projectId: draftProjectId,
|
||||
directory: normalizePath(draftDirectoryOverride ?? created.directory ?? null),
|
||||
const createdDraftSession = await materializeOpenDraftSession({
|
||||
providerID,
|
||||
modelID,
|
||||
agent: trimmedAgent,
|
||||
variant,
|
||||
})
|
||||
if (!createdDraftSession) throw new Error("Failed to create session")
|
||||
|
||||
const draftSyntheticParts = draft.syntheticParts
|
||||
const createdDirectory = normalizePath(draftDirectoryOverride ?? created.directory ?? null)
|
||||
const configState = useConfigStore.getState()
|
||||
void activateConfigForDirectory(createdDirectory).catch((error) => {
|
||||
console.warn("Failed to activate directory after creating session:", error)
|
||||
})
|
||||
|
||||
const effectiveDraftAgent = trimmedAgent ?? configState.currentAgentName
|
||||
|
||||
useSelectionStore.getState().saveSessionModelSelection(created.id, providerID, modelID)
|
||||
|
||||
if (effectiveDraftAgent) {
|
||||
useSelectionStore.getState().saveSessionAgentSelection(created.id, effectiveDraftAgent)
|
||||
useSelectionStore.getState().saveAgentModelForSession(created.id, effectiveDraftAgent, providerID, modelID)
|
||||
useSelectionStore.getState().saveAgentModelVariantForSession(created.id, effectiveDraftAgent, providerID, modelID, variant)
|
||||
}
|
||||
|
||||
get().initializeNewOpenChamberSession(created.id, configState.agents ?? [])
|
||||
|
||||
get().closeNewSessionDraft()
|
||||
get().setCurrentSession(created.id, createdDirectory)
|
||||
|
||||
if (draftTargetFolderId) {
|
||||
const scopeKey = draftDirectoryOverride || created.directory || null
|
||||
if (scopeKey) {
|
||||
useSessionFoldersStore.getState().addSessionToFolder(scopeKey, draftTargetFolderId, created.id)
|
||||
}
|
||||
}
|
||||
|
||||
const mergedAdditionalParts = draftSyntheticParts?.length
|
||||
? [...(additionalParts || []), ...draftSyntheticParts]
|
||||
const mergedAdditionalParts = createdDraftSession.syntheticParts?.length
|
||||
? [...(additionalParts || []), ...createdDraftSession.syntheticParts]
|
||||
: additionalParts
|
||||
|
||||
notifyMessageSent(created.id)
|
||||
notifyMessageSent(createdDraftSession.sessionId)
|
||||
|
||||
markPendingUserSendAnimation(created.id)
|
||||
markPendingUserSendAnimation(createdDraftSession.sessionId)
|
||||
|
||||
const files = attachments?.map((a) => ({
|
||||
type: "file" as const,
|
||||
@@ -981,12 +1013,12 @@ export const useSessionUIStore = create<SessionUIState>()((set, get) => ({
|
||||
}))
|
||||
|
||||
await routeMessage({
|
||||
sessionId: created.id,
|
||||
directory: createdDirectory,
|
||||
sessionId: createdDraftSession.sessionId,
|
||||
directory: createdDraftSession.directory,
|
||||
content,
|
||||
providerID,
|
||||
modelID,
|
||||
agent: effectiveDraftAgent,
|
||||
agent: createdDraftSession.agent,
|
||||
agentMentionName,
|
||||
variant,
|
||||
inputMode,
|
||||
|
||||
Reference in New Issue
Block a user