import { type FormEvent, type UIEvent, useEffect, useLayoutEffect, } from 'react'; import { AGENT_CHAT_SCROLL_BOTTOM_THRESHOLD } from '../../app/constants'; import { useEscapeToClose } from '../../app/dialogs'; import { resolveTauriInvoke } from '../../app/tauri'; import type { AgentChatPendingRuntimeRun, AgentConversationSessionListResult, AgentConversationSessionRecord, AgentGoalRecord, AgentRuntimeResult, AgentRuntimeSteerResult, GameCreatorAgentRuntimeUpdateEvent, GameCreatorChatAgentReply, GameCreatorLlmConfigStatus, GameCreatorRoleAgentChatStreamEvent, LauncherAgentChatAgent, LocalConversationResult, TauriInvoke, } from '../../app/types'; import { type LauncherView } from '../../view/layout'; import { agentGoalStatusIsTerminal, agentRuntimeConversationStatus, agentRuntimeNeedsUserInput, agentRuntimeStartedRunId, agentRuntimeStartStatus, agentRuntimeStateFromResult, agentRuntimeSteerStatus, createAgentChatRunId, createLocalConversationDraftMessage, isAgentRuntimeTerminalState, isMissingAgentGoalCommandError, isMissingAgentRuntimeResumeCommandError, isMissingAgentSessionCommandError, matchingAgentRuntimeForSteer, mergeAgentGoalRecordFromRuntime, normalizeAgentRuntimeState, } from '../agent-runtime'; import { formatAgentLlmConfigWarning, formatCodexAgentModeLabel, formatCodexRuntimeCapabilities, isCodexAgentMode, llmStatusForAgentCard, } from '../project-summary/agentPresentation'; import { isAbsoluteProjectPath, projectPathHasControlCharacter, } from '../project-summary/projectSummary'; import { createDeveloperAgentControls } from './developerAgentControls'; import { useDeveloperAgentState } from './useDeveloperAgentState'; export function useDeveloperAgentPanel(launcherView: LauncherView) { const state = useDeveloperAgentState(); const { launcherAgentChatAgents, agentChatProjectPath, setAgentChatProjectPath, agentChatSelectedAgentId, setAgentChatSelectedAgentId, agentChatMessages, setAgentChatMessages, agentChatSessions, setAgentChatSessions, agentChatSelectedSessionId, setAgentChatSelectedSessionId, agentChatActiveSessionId, setAgentChatActiveSessionId, agentChatLegacySessionMode, setAgentChatLegacySessionMode, agentChatConversationPath, setAgentChatConversationPath, agentChatSessionStatus, setAgentChatSessionStatus, agentChatInput, setAgentChatInput, agentChatInteractionMode, setAgentChatInteractionMode, agentChatRunSubmitMode, setAgentChatRunSubmitMode, agentChatReplyPhase, setAgentChatReplyPhase, agentChatStatus, setAgentChatStatus, agentChatBusy, setAgentChatBusy, agentChatMessagesRef, agentChatShouldFollowLatestRef, agentChatBackgroundBusy, setAgentChatBackgroundBusy, agentChatPendingRuntimeRun, setAgentChatPendingRuntimeRunState, agentChatLlmConfigStatus, setAgentChatLlmConfigStatus, agentChatLlmStatus, setAgentChatLlmStatus, agentChatRuntime, setAgentChatRuntime, agentChatActiveRuntime, setAgentChatActiveRuntime, agentChatRuntimeError, setAgentChatRuntimeError, agentChatGoal, setAgentChatGoal, agentChatGoalError, setAgentChatGoalError, agentChatGoalDialog, setAgentChatGoalDialog, agentChatGoalDialogError, setAgentChatGoalDialogError, agentChatResumeConfirmation, setAgentChatResumeConfirmation, agentChatLoadVersionRef, agentChatRuntimeResumeProjectPathRef, agentChatProjectPathRef, agentChatSelectedAgentIdRef, agentChatSelectedSessionIdRef, agentChatActiveSessionIdRef, agentChatPendingRuntimeRunRef, agentChatRuntimeSyncingRunIdsRef, agentChatRuntimeSyncConversationRef, } = state; useEffect(() => { setAgentChatRunSubmitMode('steer'); }, [ agentChatSelectedAgentId, agentChatSelectedSessionId, setAgentChatRunSubmitMode, ]); function setAgentChatPendingRuntimeRun( pendingRun: AgentChatPendingRuntimeRun | null, ) { agentChatPendingRuntimeRunRef.current = pendingRun; setAgentChatPendingRuntimeRunState(pendingRun); } async function syncAgentChatConversationAfterRuntime( invoke: TauriInvoke, pendingRun: AgentChatPendingRuntimeRun, ) { let latestResult: LocalConversationResult | null = null; for (let attempt = 0; attempt < 4; attempt += 1) { if ( agentChatProjectPathRef.current.trim() !== pendingRun.projectPath || agentChatSelectedAgentIdRef.current !== pendingRun.agentId || (pendingRun.sessionId !== null && agentChatSelectedSessionIdRef.current !== pendingRun.sessionId) ) { return; } try { latestResult = await invoke( 'read_local_conversation', { projectPath: pendingRun.projectPath, agentId: pendingRun.agentId, ...(pendingRun.sessionId ? { sessionId: pendingRun.sessionId } : {}), }, ); } catch (error) { if (attempt === 3) { setAgentChatStatus( `Agent 已结束,但同步对话失败:${ error instanceof Error ? error.message : String(error) }`, ); break; } } const newMessages = latestResult?.messages.slice(pendingRun.messageCount) ?? []; if ( newMessages.some((message) => message.role === 'assistant') || attempt === 3 ) { break; } await new Promise((resolve) => { window.setTimeout(resolve, 40); }); } if ( latestResult && agentChatProjectPathRef.current.trim() === pendingRun.projectPath && agentChatSelectedAgentIdRef.current === pendingRun.agentId && (pendingRun.sessionId === null || agentChatSelectedSessionIdRef.current === pendingRun.sessionId) ) { setAgentChatMessages(latestResult.messages); setAgentChatConversationPath(latestResult.path); updateAgentChatSessionMessageCount( pendingRun.sessionId, latestResult.messages.length, ); setAgentChatStatus( `已同步 ${latestResult.messages.length} 条:${latestResult.path}`, ); } if (pendingRun.sessionId) { try { const goal = await invoke( 'read_game_creator_agent_goal', { projectPath: pendingRun.projectPath, agentId: pendingRun.agentId, sessionId: pendingRun.sessionId, }, ); if ( agentChatProjectPathRef.current.trim() === pendingRun.projectPath && agentChatSelectedAgentIdRef.current === pendingRun.agentId && agentChatSelectedSessionIdRef.current === pendingRun.sessionId ) { setAgentChatGoal(goal); setAgentChatGoalError(''); } } catch (error) { if ( !isMissingAgentGoalCommandError(error) && agentChatProjectPathRef.current.trim() === pendingRun.projectPath && agentChatSelectedAgentIdRef.current === pendingRun.agentId && agentChatSelectedSessionIdRef.current === pendingRun.sessionId ) { setAgentChatGoalError( `Goal 状态读取失败:${ error instanceof Error ? error.message : String(error) }`, ); } } } if (agentChatPendingRuntimeRunRef.current?.runId === pendingRun.runId) { setAgentChatPendingRuntimeRun(null); } } agentChatRuntimeSyncConversationRef.current = syncAgentChatConversationAfterRuntime; useLayoutEffect(() => { const messageList = agentChatMessagesRef.current; if (messageList && agentChatShouldFollowLatestRef.current) { messageList.scrollTop = messageList.scrollHeight; } }, [ agentChatMessages, agentChatPendingRuntimeRun, agentChatReplyPhase, agentChatStatus, agentChatMessagesRef, agentChatShouldFollowLatestRef, ]); function handleAgentChatMessagesScroll(event: UIEvent) { const messageList = event.currentTarget; const distanceFromBottom = messageList.scrollHeight - messageList.scrollTop - messageList.clientHeight; agentChatShouldFollowLatestRef.current = distanceFromBottom <= AGENT_CHAT_SCROLL_BOTTOM_THRESHOLD; } useEffect(() => { const listen = window.__TAURI__?.event?.listen; const invoke = resolveTauriInvoke(); // WorkspaceLauncher stays mounted while users work in a project. The // legacy developer Agent Runtime listener must therefore exist only on // the explicit developer Agent chat surface, never behind direct Codex // project workbenches. if (launcherView !== 'agent-chat' || !listen || !invoke) { return; } let cleanup: (() => void) | null = null; let disposed = false; void listen( 'game-creator-agent-runtime-update', (event) => { const payload = event.payload; if ( payload.projectPath !== agentChatProjectPathRef.current || payload.agentId !== agentChatSelectedAgentIdRef.current ) { return; } const runtimeState = agentRuntimeStateFromResult(payload.runtime); if ( agentChatActiveSessionIdRef.current === null || payload.runtime.state.sessionId === agentChatActiveSessionIdRef.current ) { setAgentChatActiveRuntime((current) => agentRuntimeStateFromResult(payload.runtime, current), ); } if ( agentChatSelectedSessionIdRef.current !== null && payload.runtime.state.sessionId !== agentChatSelectedSessionIdRef.current ) { return; } setAgentChatRuntime((current) => agentRuntimeStateFromResult(payload.runtime, current), ); setAgentChatGoal((current) => mergeAgentGoalRecordFromRuntime(current, runtimeState), ); setAgentChatRuntimeError(''); const pendingRun = agentChatPendingRuntimeRunRef.current; if ( !pendingRun || pendingRun.projectPath !== payload.projectPath || pendingRun.agentId !== payload.agentId || pendingRun.runId !== payload.runId || (pendingRun.sessionId !== null && pendingRun.sessionId !== payload.runtime.state.sessionId) ) { return; } setAgentChatStatus(agentRuntimeConversationStatus(runtimeState)); if ( isAgentRuntimeTerminalState(runtimeState) && !agentChatRuntimeSyncingRunIdsRef.current.has(pendingRun.runId) ) { const syncConversation = agentChatRuntimeSyncConversationRef.current; if (!syncConversation) { return; } agentChatRuntimeSyncingRunIdsRef.current.add(pendingRun.runId); void syncConversation(invoke, pendingRun).finally(() => { agentChatRuntimeSyncingRunIdsRef.current.delete(pendingRun.runId); }); } }, ) .then((unlisten) => { if (disposed) { unlisten(); return; } cleanup = unlisten; }) .catch((error) => { if (disposed) { return; } setAgentChatRuntimeError( `Runtime 订阅不可用:${ error instanceof Error ? error.message : String(error) }`, ); }); return () => { disposed = true; cleanup?.(); }; }, [ agentChatActiveSessionIdRef, agentChatPendingRuntimeRunRef, agentChatProjectPathRef, agentChatRuntimeSyncConversationRef, agentChatRuntimeSyncingRunIdsRef, agentChatSelectedAgentIdRef, agentChatSelectedSessionIdRef, launcherView, setAgentChatActiveRuntime, setAgentChatGoal, setAgentChatRuntime, setAgentChatRuntimeError, setAgentChatStatus, ]); useEffect(() => { const invoke = resolveTauriInvoke(); const pendingRun = agentChatPendingRuntimeRun; if (!invoke || !pendingRun) { return; } let disposed = false; let inFlight = false; const pollRuntime = async () => { if (disposed || inFlight) { return; } inFlight = true; try { const runtime = await invoke( 'read_game_creator_agent_runtime', { projectPath: pendingRun.projectPath, agentId: pendingRun.agentId, ...(pendingRun.sessionId ? { sessionId: pendingRun.sessionId } : {}), }, ); if ( disposed || agentChatPendingRuntimeRunRef.current?.runId !== pendingRun.runId || agentChatProjectPathRef.current.trim() !== pendingRun.projectPath || agentChatSelectedAgentIdRef.current !== pendingRun.agentId ) { return; } const runtimeState = agentRuntimeStateFromResult(runtime); const tracksPendingRun = runtimeState.runId === pendingRun.runId || (runtimeState.recentTasks ?? []).some( (task) => task.runId === pendingRun.runId, ); if (!tracksPendingRun) { return; } if ( pendingRun.sessionId !== null && runtimeState.sessionId !== pendingRun.sessionId ) { return; } setAgentChatRuntime((current) => normalizeAgentRuntimeState(runtimeState, current), ); setAgentChatGoal((current) => mergeAgentGoalRecordFromRuntime(current, runtimeState), ); if ( agentChatActiveSessionIdRef.current === null || runtimeState.sessionId === agentChatActiveSessionIdRef.current ) { setAgentChatActiveRuntime((current) => normalizeAgentRuntimeState(runtimeState, current), ); } setAgentChatRuntimeError(''); setAgentChatStatus(agentRuntimeConversationStatus(runtimeState)); if ( isAgentRuntimeTerminalState(runtimeState) && !agentChatRuntimeSyncingRunIdsRef.current.has(pendingRun.runId) ) { const syncConversation = agentChatRuntimeSyncConversationRef.current; if (!syncConversation) { return; } agentChatRuntimeSyncingRunIdsRef.current.add(pendingRun.runId); void syncConversation(invoke, pendingRun).finally(() => { agentChatRuntimeSyncingRunIdsRef.current.delete(pendingRun.runId); }); } } catch (error) { if (!disposed) { setAgentChatRuntimeError( `Runtime 状态刷新失败:${ error instanceof Error ? error.message : String(error) }`, ); } } finally { inFlight = false; } }; void pollRuntime(); const timer = window.setInterval(() => { void pollRuntime(); }, 750); return () => { disposed = true; window.clearInterval(timer); }; }, [ agentChatActiveSessionIdRef, agentChatPendingRuntimeRun, agentChatPendingRuntimeRunRef, agentChatProjectPathRef, agentChatRuntimeSyncConversationRef, agentChatRuntimeSyncingRunIdsRef, agentChatSelectedAgentIdRef, setAgentChatActiveRuntime, setAgentChatGoal, setAgentChatRuntime, setAgentChatRuntimeError, setAgentChatStatus, ]); useEffect(() => { if (launcherView !== 'agent-chat') { return; } void loadAgentChatLlmStatus(); // Reload only when the active launcher selection changes. // eslint-disable-next-line react-hooks/exhaustive-deps }, [launcherView, agentChatSelectedAgentId]); function validateAgentChatProjectPath(projectPath = agentChatProjectPath) { const trimmedProjectPath = projectPath.trim(); if (!trimmedProjectPath || !isAbsoluteProjectPath(trimmedProjectPath)) { setAgentChatStatus('请提供项目绝对路径'); return null; } if (projectPathHasControlCharacter(trimmedProjectPath)) { setAgentChatStatus('项目目录不能包含控制字符'); return null; } return trimmedProjectPath; } function selectedLauncherAgentChatAgent( agentId = agentChatSelectedAgentId, ): LauncherAgentChatAgent | null { return ( launcherAgentChatAgents.find((agent) => agent.id === agentId) ?? launcherAgentChatAgents[0] ?? null ); } function selectedLauncherAgentChatSession( sessionId = agentChatSelectedSessionId, ): AgentConversationSessionRecord | null { if (!sessionId) { return null; } return ( agentChatSessions.find((session) => session.sessionId === sessionId) ?? null ); } function agentChatSessionInvokeArgs(sessionId: string | null) { return sessionId ? { sessionId } : {}; } function resetAgentChatSessionView() { agentChatShouldFollowLatestRef.current = true; setAgentChatReplyPhase('idle'); setAgentChatPendingRuntimeRun(null); setAgentChatRunSubmitMode('steer'); setAgentChatSessions([]); setAgentChatSelectedSessionId(null); setAgentChatActiveSessionId(null); setAgentChatLegacySessionMode(false); setAgentChatActiveRuntime(null); setAgentChatRuntime(null); setAgentChatRuntimeError(''); setAgentChatGoal(null); setAgentChatGoalError(''); setAgentChatGoalDialog(null); setAgentChatGoalDialogError(''); setAgentChatConversationPath(''); setAgentChatSessionStatus(''); setAgentChatMessages([]); } function updateAgentChatSessionMessageCount( sessionId: string | null, messageCount: number, ) { if (!sessionId) { return; } setAgentChatSessions((sessions) => sessions.map((session) => session.sessionId === sessionId ? { ...session, messageCount, updatedAt: Date.now() } : session, ), ); } function getCurrentAgentChatLlmWarning( agent = selectedLauncherAgentChatAgent(), ) { if (!agent) { return null; } return formatAgentLlmConfigWarning(agentChatLlmConfigStatus, agent); } async function loadAgentChatLlmStatus() { const invoke = resolveTauriInvoke(); if (!invoke) { setAgentChatLlmConfigStatus(null); setAgentChatLlmStatus('需要在 Tauri App 内检查 LLM 配置'); return; } setAgentChatLlmStatus('正在检查 LLM 配置'); try { const status = await invoke( 'check_game_creator_llm_config', ); setAgentChatLlmConfigStatus(status); const agent = selectedLauncherAgentChatAgent(); const agentStatus = agent ? llmStatusForAgentCard(status, agent) : null; if (!agentStatus) { setAgentChatLlmStatus('未找到当前 Agent 的 LLM 路由'); return; } setAgentChatLlmStatus( isCodexAgentMode(agentStatus.agentMode) ? agentStatus.configured ? `当前 Agent 已检测到 ${formatCodexAgentModeLabel( agentStatus.agentMode, )};${formatCodexRuntimeCapabilities(agentStatus)};登录与网络将在首次调用时验证` : `当前 Agent ${formatCodexAgentModeLabel( agentStatus.agentMode, )} 未就绪;${formatCodexRuntimeCapabilities(agentStatus)}:${ agentStatus.error ?? 'Codex CLI 不可用' }` : agentStatus.configured ? `当前 Agent LLM 已配置:${agentStatus.model ?? '未命名模型'}${ agentStatus.reasoningEffort ? `,推理 ${agentStatus.reasoningEffort}` : '' },联网检索 ${agentStatus.webSearchEnabled ? '开启' : '关闭'},API Key ${ agentStatus.apiKeyPresent ? '已读取' : '未读取' }` : `当前 Agent LLM 未就绪:${ agentStatus.error ?? '缺少 API Key 或模型配置' }${ agentStatus.reasoningEffort ? `(推理 ${agentStatus.reasoningEffort})` : '' },联网检索 ${agentStatus.webSearchEnabled ? '开启' : '关闭'}`, ); } catch (error) { setAgentChatLlmConfigStatus(null); setAgentChatLlmStatus( `LLM 状态读取失败:${error instanceof Error ? error.message : String(error)}`, ); } } function cancelAgentChatRuntimeResume() { setAgentChatResumeConfirmation(null); setAgentChatStatus('已取消恢复 Agent Runtime 任务'); } async function confirmAgentChatRuntimeResume() { const pending = agentChatResumeConfirmation; const invoke = resolveTauriInvoke(); if (!pending || !invoke) { return; } setAgentChatResumeConfirmation(null); setAgentChatStatus('正在恢复 Agent Runtime 任务'); try { await invoke( 'confirm_resume_game_creator_agent_runtime_tasks', { projectPath: pending.projectPath }, ); if (agentChatProjectPathRef.current.trim() !== pending.projectPath) { return; } agentChatRuntimeResumeProjectPathRef.current = pending.projectPath; await loadAgentChatConversation( agentChatSelectedAgentIdRef.current, pending.projectPath, agentChatSelectedSessionIdRef.current, ); setAgentChatStatus('已确认恢复 Agent Runtime 任务'); } catch (error) { if (agentChatProjectPathRef.current.trim() !== pending.projectPath) { return; } agentChatRuntimeResumeProjectPathRef.current = null; setAgentChatStatus( `Agent Runtime 恢复失败:${ error instanceof Error ? error.message : String(error) }`, ); } } async function resumeAgentChatRuntimeTasksIfNeeded( invoke: TauriInvoke, projectPathForChat: string, ) { if (agentChatRuntimeResumeProjectPathRef.current === projectPathForChat) { return ''; } try { await invoke( 'resume_game_creator_agent_runtime_tasks', { projectPath: projectPathForChat }, ); agentChatRuntimeResumeProjectPathRef.current = projectPathForChat; return ''; } catch (error) { const message = error instanceof Error ? error.message : String(error); if (isMissingAgentRuntimeResumeCommandError(error)) { agentChatRuntimeResumeProjectPathRef.current = projectPathForChat; return ''; } if (message.includes('项目权限策略要求用户确认:agent.resume')) { agentChatRuntimeResumeProjectPathRef.current = projectPathForChat; setAgentChatResumeConfirmation({ projectPath: projectPathForChat, detail: `恢复 ${projectPathForChat} 中未完成的 Agent Runtime 任务`, }); return ';等待确认恢复 Agent Runtime 任务'; } if (message.includes('项目权限策略拒绝执行:agent.resume')) { agentChatRuntimeResumeProjectPathRef.current = projectPathForChat; return ';项目策略禁止恢复 Agent Runtime 任务'; } agentChatRuntimeResumeProjectPathRef.current = null; return `;Agent Runtime 恢复检查失败:${message}`; } } useEscapeToClose( cancelAgentChatRuntimeResume, agentChatResumeConfirmation !== null, ); function closeAgentChatGoalDialog() { if (agentChatBackgroundBusy) { return; } setAgentChatGoalDialog(null); setAgentChatGoalDialogError(''); } useEscapeToClose( closeAgentChatGoalDialog, agentChatGoalDialog !== null && !agentChatBackgroundBusy, ); async function handleOpenProjectSupervisorChatWindow() { if (agentChatBusy || agentChatBackgroundBusy) { return; } const projectPathForChat = validateAgentChatProjectPath(); if (!projectPathForChat) { return; } const invoke = resolveTauriInvoke(); if (!invoke) { setAgentChatStatus('需要在 Tauri App 内运行'); return; } setAgentChatStatus('正在打开项目总控对话'); try { await invoke('open_project_supervisor_chat_window', { projectPath: projectPathForChat, }); setAgentChatStatus('已打开项目总控对话'); } catch (error) { setAgentChatStatus( error instanceof Error ? error.message : String(error), ); } } async function handleAgentChatPickProjectDirectory() { if (agentChatBusy || agentChatBackgroundBusy) { return; } const invoke = resolveTauriInvoke(); if (!invoke) { setAgentChatStatus('需要在 Tauri App 内运行'); return; } setAgentChatStatus('正在选择'); try { const selectedPath = await invoke( 'pick_local_project_directory', agentChatProjectPath.trim() ? { initialPath: agentChatProjectPath.trim() } : undefined, ); if (!selectedPath) { setAgentChatStatus('已取消'); return; } setAgentChatStatus('已选择项目目录'); resetAgentChatSessionView(); agentChatRuntimeResumeProjectPathRef.current = null; setAgentChatResumeConfirmation(null); setAgentChatProjectPath(selectedPath); void loadAgentChatConversation(agentChatSelectedAgentId, selectedPath); } catch (error) { setAgentChatStatus( error instanceof Error ? error.message : String(error), ); } } async function loadAgentChatConversation( agentId = agentChatSelectedAgentId, projectPath = agentChatProjectPath, requestedSessionId?: string | null, knownSessions?: AgentConversationSessionListResult | null, ) { const projectPathForChat = validateAgentChatProjectPath(projectPath); const agent = selectedLauncherAgentChatAgent(agentId); if (!projectPathForChat || !agent) { return; } agentChatShouldFollowLatestRef.current = true; const pendingRun = agentChatPendingRuntimeRunRef.current; if ( pendingRun && (pendingRun.projectPath !== projectPathForChat || pendingRun.agentId !== agent.id || (requestedSessionId !== undefined && pendingRun.sessionId !== requestedSessionId)) ) { setAgentChatPendingRuntimeRun(null); } const invoke = resolveTauriInvoke(); if (!invoke) { setAgentChatStatus('需要在 Tauri App 内运行'); return; } const loadVersion = agentChatLoadVersionRef.current + 1; agentChatLoadVersionRef.current = loadVersion; setAgentChatBusy(true); setAgentChatGoal(null); setAgentChatGoalError(''); setAgentChatStatus('正在读取'); try { const runtimeResumeStatus = await resumeAgentChatRuntimeTasksIfNeeded( invoke, projectPathForChat, ); if (agentChatLoadVersionRef.current !== loadVersion) { return; } let sessionList = knownSessions; let sessionListError = ''; if (sessionList === undefined) { try { const candidate = await invoke( 'list_game_creator_agent_sessions', { projectPath: projectPathForChat, agentId: agent.id, }, ); if (candidate === undefined || candidate === null) { sessionList = null; sessionListError = '当前客户端后端不支持多会话命令'; } else if (!Array.isArray(candidate.sessions)) { setAgentChatSessionStatus('Agent 会话列表返回格式无效'); setAgentChatStatus('Agent 会话列表返回格式无效,已停止读取'); return; } else { sessionList = candidate; } } catch (error) { if (!isMissingAgentSessionCommandError(error)) { const message = error instanceof Error ? error.message : String(error); setAgentChatSessionStatus(`Agent 会话列表读取失败:${message}`); setAgentChatStatus(`Agent 会话列表读取失败:${message}`); return; } sessionList = null; sessionListError = '当前客户端后端不支持多会话命令'; } } if (agentChatLoadVersionRef.current !== loadVersion) { return; } let sessionId = requestedSessionId ?? null; let resolvedActiveSessionId = agentChatActiveSessionId; if (sessionList) { const requestedExists = sessionList.sessions.some( (session) => session.sessionId === sessionId, ); if (!requestedExists) { sessionId = sessionList.activeSessionId || sessionList.sessions.find((session) => session.archivedAt === null) ?.sessionId || sessionList.sessions[0]?.sessionId || null; } setAgentChatSessions(sessionList.sessions); setAgentChatSelectedSessionId(sessionId); resolvedActiveSessionId = sessionList.activeSessionId || null; setAgentChatActiveSessionId(resolvedActiveSessionId); setAgentChatLegacySessionMode(false); setAgentChatSessionStatus( sessionList.sessions.length > 0 ? `共 ${sessionList.sessions.length} 个会话` : '暂无会话', ); } else { sessionId = null; resolvedActiveSessionId = null; setAgentChatSessions([]); setAgentChatSelectedSessionId(null); setAgentChatActiveSessionId(null); setAgentChatLegacySessionMode(true); setAgentChatSessionStatus( sessionListError ? `会话列表不可用,已使用旧版单会话:${sessionListError}` : '已使用旧版单会话', ); } const selectedSessionIsActive = sessionId === resolvedActiveSessionId || (sessionId === null && resolvedActiveSessionId === null); setAgentChatRuntime(null); setAgentChatRuntimeError(''); if (sessionId) { try { const goal = await invoke( 'read_game_creator_agent_goal', { projectPath: projectPathForChat, agentId: agent.id, sessionId, }, ); if (agentChatLoadVersionRef.current !== loadVersion) { return; } setAgentChatGoal(goal); setAgentChatGoalError(''); } catch (error) { if (agentChatLoadVersionRef.current !== loadVersion) { return; } setAgentChatGoal(null); setAgentChatGoalError( isMissingAgentGoalCommandError(error) ? '' : `Goal 状态读取失败:${ error instanceof Error ? error.message : String(error) }`, ); } } const result = await invoke( 'read_local_conversation', { projectPath: projectPathForChat, agentId: agent.id, ...agentChatSessionInvokeArgs(sessionId), }, ); if (agentChatLoadVersionRef.current !== loadVersion) { return; } setAgentChatMessages(result.messages); updateAgentChatSessionMessageCount(sessionId, result.messages.length); setAgentChatConversationPath(result.path); try { const runtime = await invoke( 'read_game_creator_agent_runtime', { projectPath: projectPathForChat, agentId: agent.id, ...agentChatSessionInvokeArgs(sessionId), }, ); if (agentChatLoadVersionRef.current === loadVersion) { const runtimeState = agentRuntimeStateFromResult(runtime); setAgentChatRuntime(runtimeState); if (selectedSessionIsActive) { setAgentChatActiveRuntime(runtimeState); } setAgentChatGoal((current) => mergeAgentGoalRecordFromRuntime(current, runtimeState), ); setAgentChatRuntimeError(''); } } catch (error) { if (agentChatLoadVersionRef.current === loadVersion) { setAgentChatRuntime(null); if (selectedSessionIsActive) { setAgentChatActiveRuntime(null); } setAgentChatRuntimeError( error instanceof Error ? error.message : String(error), ); } } setAgentChatStatus( `已读取 ${result.messages.length} 条:${result.path}${runtimeResumeStatus}`, ); } catch (error) { if (agentChatLoadVersionRef.current !== loadVersion) { return; } setAgentChatMessages([]); setAgentChatConversationPath(''); setAgentChatGoal(null); setAgentChatStatus( error instanceof Error ? error.message : String(error), ); } finally { if (agentChatLoadVersionRef.current === loadVersion) { setAgentChatBusy(false); } } } async function handleAgentChatSelectSession( session: AgentConversationSessionRecord, ) { const projectPathForChat = validateAgentChatProjectPath(); const agent = selectedLauncherAgentChatAgent(); if ( !projectPathForChat || !agent || agentChatBusy || agentChatBackgroundBusy ) { return; } const invoke = resolveTauriInvoke(); if (!invoke) { setAgentChatStatus('需要在 Tauri App 内运行'); return; } if (session.archivedAt !== null) { await loadAgentChatConversation( agent.id, projectPathForChat, session.sessionId, { path: '', agentId: agent.id, activeSessionId: agentChatActiveSessionId ?? agentChatSessions.find((candidate) => candidate.archivedAt === null) ?.sessionId ?? session.sessionId, sessions: agentChatSessions, }, ); return; } if ( session.sessionId === agentChatSelectedSessionId || session.sessionId === agentChatActiveSessionId ) { await loadAgentChatConversation( agent.id, projectPathForChat, session.sessionId, { path: '', agentId: agent.id, activeSessionId: session.sessionId, sessions: agentChatSessions, }, ); return; } setAgentChatBusy(true); setAgentChatStatus('正在切换会话'); try { const result = await invoke( 'set_active_game_creator_agent_session', { projectPath: projectPathForChat, agentId: agent.id, sessionId: session.sessionId, }, ); await loadAgentChatConversation( agent.id, projectPathForChat, session.sessionId, result, ); } catch (error) { setAgentChatStatus( error instanceof Error ? error.message : String(error), ); setAgentChatBusy(false); } } async function handleAgentChatCreateSession() { const projectPathForChat = validateAgentChatProjectPath(); const agent = selectedLauncherAgentChatAgent(); if ( !projectPathForChat || !agent || agentChatBusy || agentChatBackgroundBusy ) { return; } const invoke = resolveTauriInvoke(); if (!invoke) { setAgentChatStatus('需要在 Tauri App 内运行'); return; } setAgentChatBusy(true); setAgentChatStatus('正在新建会话'); try { const result = await invoke( 'create_game_creator_agent_session', { projectPath: projectPathForChat, agentId: agent.id, title: '', }, ); await loadAgentChatConversation( agent.id, projectPathForChat, result.activeSessionId, result, ); } catch (error) { setAgentChatStatus( error instanceof Error ? error.message : String(error), ); setAgentChatBusy(false); } } async function handleAgentChatForkSession() { const projectPathForChat = validateAgentChatProjectPath(); const agent = selectedLauncherAgentChatAgent(); const sourceSession = selectedLauncherAgentChatSession(); if ( !projectPathForChat || !agent || !sourceSession || agentChatBusy || agentChatBackgroundBusy ) { return; } const invoke = resolveTauriInvoke(); if (!invoke) { setAgentChatStatus('需要在 Tauri App 内运行'); return; } setAgentChatBusy(true); setAgentChatStatus('正在分叉会话'); try { const result = await invoke( 'fork_game_creator_agent_session', { projectPath: projectPathForChat, agentId: agent.id, sourceSessionId: sourceSession.sessionId, title: '', }, ); await loadAgentChatConversation( agent.id, projectPathForChat, result.activeSessionId, result, ); setAgentChatStatus(`已从“${sourceSession.title}”分叉新会话`); } catch (error) { setAgentChatStatus( error instanceof Error ? error.message : String(error), ); setAgentChatBusy(false); } } async function handleAgentChatArchiveSession() { const projectPathForChat = validateAgentChatProjectPath(); const agent = selectedLauncherAgentChatAgent(); const session = selectedLauncherAgentChatSession(); if ( !projectPathForChat || !agent || !session || session.legacy || session.archivedAt !== null || agentChatBusy || agentChatBackgroundBusy ) { return; } const invoke = resolveTauriInvoke(); if (!invoke) { setAgentChatStatus('需要在 Tauri App 内运行'); return; } setAgentChatBusy(true); setAgentChatStatus('正在归档会话'); try { const result = await invoke( 'archive_game_creator_agent_session', { projectPath: projectPathForChat, agentId: agent.id, sessionId: session.sessionId, }, ); await loadAgentChatConversation( agent.id, projectPathForChat, result.activeSessionId, result, ); } catch (error) { setAgentChatStatus( error instanceof Error ? error.message : String(error), ); setAgentChatBusy(false); } } async function handleAgentChatSubmit(event: FormEvent) { event.preventDefault(); const projectPathForChat = validateAgentChatProjectPath(); const agent = selectedLauncherAgentChatAgent(); const sessionIdForChat = agentChatSelectedSessionId; const selectedSession = selectedLauncherAgentChatSession(sessionIdForChat); const content = agentChatInput.trim(); if (agentRuntimeNeedsUserInput(agentChatRuntime)) { setAgentChatStatus('请先回答 Agent 当前的澄清问题'); return; } if ( !projectPathForChat || !agent || !content || agentChatBusy || agentChatBackgroundBusy ) { return; } if (selectedSession && selectedSession.archivedAt !== null) { setAgentChatStatus('已归档会话只能查看,请先新建或切换到活动会话'); return; } const llmWarning = getCurrentAgentChatLlmWarning(agent); if (llmWarning) { setAgentChatStatus(llmWarning); return; } const invoke = resolveTauriInvoke(); if (!invoke) { setAgentChatStatus('需要在 Tauri App 内运行'); return; } const saveVersion = agentChatLoadVersionRef.current + 1; agentChatLoadVersionRef.current = saveVersion; agentChatShouldFollowLatestRef.current = true; setAgentChatBusy(true); setAgentChatReplyPhase('saving-user'); setAgentChatInput(''); setAgentChatStatus('正在保存用户消息'); let savedUserResult: LocalConversationResult | null = null; let stopStreamListen: (() => void) | null = null; let streamListenDisposed = false; let streamListenReady = false; let streamListenUnavailable = false; let streamFrameId: number | null = null; let pendingStreamDraftText = ''; let pendingStreamFinishReason: string | null = null; const streamDraftUpdatedAt = Date.now(); try { savedUserResult = await invoke( 'append_local_conversation_message', { projectPath: projectPathForChat, agentId: agent.id, ...agentChatSessionInvokeArgs(sessionIdForChat), message: { role: 'user', content, agentId: null, }, }, ); if (agentChatLoadVersionRef.current !== saveVersion) { return; } const savedUserMessages = savedUserResult.messages; setAgentChatMessages(savedUserMessages); updateAgentChatSessionMessageCount( sessionIdForChat, savedUserMessages.length, ); setAgentChatReplyPhase('connecting'); setAgentChatStatus('正在连接 Agent LLM'); const streamRunId = createAgentChatRunId('launcher-agent-chat'); const listen = window.__TAURI__?.event?.listen; if (listen) { try { stopStreamListen = await listen( 'game-creator-role-agent-chat-stream', (event) => { const payload = event.payload; if ( payload.projectPath !== projectPathForChat || payload.agentId !== agent.id || payload.runId !== streamRunId || agentChatLoadVersionRef.current !== saveVersion ) { return; } if (payload.runtimeState && payload.status !== 'delta') { setAgentChatRuntime((current) => normalizeAgentRuntimeState(payload.runtimeState!, current), ); setAgentChatActiveRuntime((current) => normalizeAgentRuntimeState(payload.runtimeState!, current), ); setAgentChatRuntimeError(''); } if (payload.status === 'started') { setAgentChatReplyPhase('waiting-first-content'); setAgentChatStatus( payload.runtimeSummary ? `已连接 Agent LLM,${payload.runtimeSummary}` : '已连接 Agent LLM,正在等待首个回复片段', ); return; } if (payload.status === 'delta') { setAgentChatReplyPhase('streaming'); const draftText = payload.accumulatedText || payload.deltaText; if (draftText) { pendingStreamDraftText = draftText; } pendingStreamFinishReason = payload.finishReason ?? null; if (streamFrameId === null) { streamFrameId = window.requestAnimationFrame(() => { streamFrameId = null; if (agentChatLoadVersionRef.current !== saveVersion) { return; } if (pendingStreamDraftText) { setAgentChatMessages([ ...savedUserMessages, createLocalConversationDraftMessage( pendingStreamDraftText, streamDraftUpdatedAt, ), ]); } setAgentChatStatus( pendingStreamFinishReason ? `Agent 回复结束:${pendingStreamFinishReason}` : '正在接收 Agent 回复', ); }); } return; } if (payload.status === 'completed') { if (streamFrameId !== null) { window.cancelAnimationFrame(streamFrameId); streamFrameId = null; } setAgentChatReplyPhase('saving-reply'); setAgentChatStatus( payload.runtimeSummary ?? 'Agent 回复完成,正在保存', ); return; } if (payload.status === 'failed') { if (streamFrameId !== null) { window.cancelAnimationFrame(streamFrameId); streamFrameId = null; } setAgentChatReplyPhase('saving-reply'); setAgentChatStatus( payload.runtimeSummary ?? 'Agent 流式回复失败,正在记录错误', ); } }, ); streamListenReady = true; if (streamListenDisposed) { stopStreamListen(); stopStreamListen = null; } } catch { streamListenUnavailable = true; setAgentChatStatus('实时状态不可用,正在使用普通回复模式'); } } setAgentChatStatus( streamListenReady ? '已连接 Agent LLM,正在等待首个回复片段' : streamListenUnavailable ? '实时状态不可用,正在使用普通回复模式' : '正在等待 Agent LLM 回复', ); setAgentChatReplyPhase('waiting-first-content'); let reply: GameCreatorChatAgentReply; if (streamListenReady) { try { reply = await invoke( 'chat_with_game_creator_role_agent_stream', { projectPath: projectPathForChat, agentId: agent.id, prompt: content, runId: streamRunId, ...agentChatSessionInvokeArgs(sessionIdForChat), }, ); } catch (error) { if (pendingStreamDraftText && pendingStreamFinishReason) { reply = { replyText: pendingStreamDraftText }; setAgentChatStatus( `Agent 回复已结束(${pendingStreamFinishReason}),正在保存已接收内容`, ); } else { throw error; } } } else { reply = await invoke( 'chat_with_game_creator_role_agent', { projectPath: projectPathForChat, agentId: agent.id, prompt: content, ...agentChatSessionInvokeArgs(sessionIdForChat), }, ); } if (agentChatLoadVersionRef.current !== saveVersion) { return; } try { const runtime = await invoke( 'read_game_creator_agent_runtime', { projectPath: projectPathForChat, agentId: agent.id, ...agentChatSessionInvokeArgs(sessionIdForChat), }, ); if (agentChatLoadVersionRef.current === saveVersion) { const runtimeState = agentRuntimeStateFromResult(runtime); setAgentChatRuntime(runtimeState); setAgentChatActiveRuntime(runtimeState); setAgentChatGoal((current) => mergeAgentGoalRecordFromRuntime(current, runtimeState), ); setAgentChatRuntimeError(''); } } catch (error) { if (agentChatLoadVersionRef.current === saveVersion) { setAgentChatRuntimeError( error instanceof Error ? error.message : String(error), ); } } if (streamFrameId !== null) { window.cancelAnimationFrame(streamFrameId); streamFrameId = null; } setAgentChatReplyPhase('saving-reply'); setAgentChatStatus('正在保存 Agent 回复'); setAgentChatMessages([ ...savedUserMessages, createLocalConversationDraftMessage( reply.replyText, streamDraftUpdatedAt, ), ]); const assistantResult = await invoke( 'append_local_conversation_message', { projectPath: projectPathForChat, agentId: agent.id, ...agentChatSessionInvokeArgs(sessionIdForChat), message: { role: 'assistant', content: reply.replyText, agentId: null, }, }, ); if (agentChatLoadVersionRef.current !== saveVersion) { return; } setAgentChatMessages(assistantResult.messages); updateAgentChatSessionMessageCount( sessionIdForChat, assistantResult.messages.length, ); setAgentChatStatus( `已保存 ${assistantResult.messages.length} 条:${assistantResult.path}`, ); } catch (error) { if (agentChatLoadVersionRef.current !== saveVersion) { return; } if (savedUserResult) { const message = `已保存用户消息;Agent 回复失败:${ error instanceof Error ? error.message : String(error) }`; try { const errorResult = await invoke( 'append_local_conversation_message', { projectPath: projectPathForChat, agentId: agent.id, ...agentChatSessionInvokeArgs(sessionIdForChat), message: { role: 'assistant', content: message, agentId: null, }, }, ); if (agentChatLoadVersionRef.current !== saveVersion) { return; } setAgentChatMessages(errorResult.messages); updateAgentChatSessionMessageCount( sessionIdForChat, errorResult.messages.length, ); } catch { setAgentChatMessages([ ...savedUserResult.messages, { schemaVersion: 'game-creator-conversation.v1', role: 'assistant', content: message, agentId: null, updatedAt: Date.now(), }, ]); } setAgentChatStatus(message); } else { setAgentChatInput(content); setAgentChatStatus( error instanceof Error ? error.message : String(error), ); } } finally { streamListenDisposed = true; stopStreamListen?.(); if (streamFrameId !== null) { window.cancelAnimationFrame(streamFrameId); } if (agentChatLoadVersionRef.current === saveVersion) { setAgentChatReplyPhase('idle'); setAgentChatBusy(false); } } } async function handleAgentChatStartBackgroundTask() { const projectPathForChat = validateAgentChatProjectPath(); const agent = selectedLauncherAgentChatAgent(); const sessionIdForTask = agentChatSelectedSessionId; const selectedSession = selectedLauncherAgentChatSession(sessionIdForTask); const content = agentChatInput.trim(); if (agentRuntimeNeedsUserInput(agentChatRuntime)) { setAgentChatStatus('请先回答 Agent 当前的澄清问题'); return; } if ( !projectPathForChat || !agent || !content || agentChatBusy || agentChatBackgroundBusy ) { return; } if (selectedSession && selectedSession.archivedAt !== null) { setAgentChatStatus('已归档会话不能启动任务,请先新建或切换会话'); return; } const llmWarning = getCurrentAgentChatLlmWarning(agent); if (llmWarning) { setAgentChatStatus(llmWarning); return; } const invoke = resolveTauriInvoke(); if (!invoke) { setAgentChatStatus('需要在 Tauri App 内运行'); return; } const saveVersion = agentChatLoadVersionRef.current + 1; agentChatLoadVersionRef.current = saveVersion; agentChatShouldFollowLatestRef.current = true; const steerRuntime = agentChatRunSubmitMode === 'steer' ? matchingAgentRuntimeForSteer( [agentChatRuntime, agentChatActiveRuntime], agent.id, sessionIdForTask, ) : null; const requestedRunId = steerRuntime?.runId ?? createAgentChatRunId('launcher-agent-task'); let pendingRunId = requestedRunId; const previousPendingRun = agentChatPendingRuntimeRunRef.current; setAgentChatBackgroundBusy(true); if (steerRuntime) { setAgentChatStatus(`正在向当前 Run 追加指令:${steerRuntime.runId}`); } else { setAgentChatInput(''); setAgentChatPendingRuntimeRun({ projectPath: projectPathForChat, agentId: agent.id, sessionId: sessionIdForTask, runId: requestedRunId, messageCount: agentChatMessages.length, }); setAgentChatStatus('正在启动 Agent 后台任务'); } try { let runtime: AgentRuntimeResult; let successStatus: string; if (steerRuntime) { const steer = await invoke( 'steer_game_creator_agent_runtime_task', { projectPath: projectPathForChat, agentId: agent.id, sessionId: steerRuntime.sessionId, runId: steerRuntime.runId, steerId: createAgentChatRunId('launcher-agent-steer'), instruction: content, }, ); runtime = steer.runtime; successStatus = agentRuntimeSteerStatus(steer); } else { runtime = await invoke( 'start_game_creator_agent_runtime_task', { projectPath: projectPathForChat, agentId: agent.id, task: content, runId: requestedRunId, ...agentChatSessionInvokeArgs(sessionIdForTask), }, ); successStatus = agentRuntimeStartStatus(runtime); } if (agentChatLoadVersionRef.current !== saveVersion) { return; } const runtimeState = agentRuntimeStateFromResult(runtime); pendingRunId = steerRuntime ? runtimeState.runId : agentRuntimeStartedRunId(runtime, requestedRunId); setAgentChatInput(''); setAgentChatRunSubmitMode('steer'); setAgentChatPendingRuntimeRun({ projectPath: projectPathForChat, agentId: agent.id, sessionId: sessionIdForTask, runId: pendingRunId, messageCount: agentChatMessages.length, }); setAgentChatRuntime(runtimeState); setAgentChatActiveRuntime(runtimeState); setAgentChatGoal((current) => mergeAgentGoalRecordFromRuntime(current, runtimeState), ); setAgentChatRuntimeError(''); try { const conversation = await invoke( 'read_local_conversation', { projectPath: projectPathForChat, agentId: agent.id, ...agentChatSessionInvokeArgs(sessionIdForTask), }, ); if (agentChatLoadVersionRef.current !== saveVersion) { return; } setAgentChatMessages(conversation.messages); if (agentChatPendingRuntimeRunRef.current?.runId === pendingRunId) { setAgentChatPendingRuntimeRun({ ...agentChatPendingRuntimeRunRef.current, messageCount: conversation.messages.length, }); } setAgentChatConversationPath(conversation.path); updateAgentChatSessionMessageCount( sessionIdForTask, conversation.messages.length, ); } catch (error) { if (agentChatLoadVersionRef.current === saveVersion) { setAgentChatRuntimeError( `对话刷新失败:${ error instanceof Error ? error.message : String(error) }`, ); } } setAgentChatStatus(successStatus); } catch (error) { if (agentChatLoadVersionRef.current !== saveVersion) { return; } if ( !steerRuntime && agentChatPendingRuntimeRunRef.current?.runId === pendingRunId ) { setAgentChatPendingRuntimeRun(previousPendingRun); } setAgentChatInput(content); setAgentChatStatus( error instanceof Error ? error.message : String(error), ); } finally { if (agentChatLoadVersionRef.current === saveVersion) { setAgentChatBackgroundBusy(false); } } } const { openAgentChatGoalDialog, handleAgentChatGoalDialogSubmit, handleAgentChatGoalControl, handleAgentChatCompactContext, handleAgentChatCancelRuntimeTask, handleAgentChatRetryRuntimeTask, handleAgentChatConfirmRuntimeTask, handleAgentChatRejectRuntimeTask, handleAgentChatSubmitUserInput, } = createDeveloperAgentControls({ state, validateAgentChatProjectPath, selectedLauncherAgentChatAgent, selectedLauncherAgentChatSession, agentChatSessionInvokeArgs, updateAgentChatSessionMessageCount, getCurrentAgentChatLlmWarning, setAgentChatPendingRuntimeRun, }); const currentAgentChatAgent = selectedLauncherAgentChatAgent(); const currentAgentChatSession = selectedLauncherAgentChatSession(); const currentAgentChatSessionArchived = currentAgentChatSession?.archivedAt !== null && currentAgentChatSession?.archivedAt !== undefined; const currentAgentChatRuntimeMatchesGoal = Boolean( agentChatRuntime?.goalId && (!agentChatGoal || agentChatRuntime.goalId === agentChatGoal.goalId), ); const currentAgentChatGoalStatus = (currentAgentChatRuntimeMatchesGoal ? agentChatRuntime?.goalStatus : null) ?? agentChatGoal?.status ?? null; const currentAgentChatGoalDialogMode: 'create' | 'edit' = !agentChatGoal || agentGoalStatusIsTerminal(currentAgentChatGoalStatus) ? 'create' : 'edit'; const currentAgentChatSessionMutationBlocked = Boolean( agentChatActiveRuntime && ([ 'running', 'pending', 'waiting-for-confirmation', 'waiting-for-user-input', 'cancelling', 'finalizing', 'pausing', 'paused', ].includes(agentChatActiveRuntime.status) || ['needs-reconciliation', 'pausing', 'paused'].includes( agentChatActiveRuntime.phase, ) || ['pause-requested', 'paused', 'clearing'].includes( agentChatActiveRuntime.goalStatus ?? '', ) || (agentChatActiveRuntime.taskQueue?.pending ?? 0) > 0 || (agentChatActiveRuntime.taskQueue?.running ?? 0) > 0 || (agentChatActiveRuntime.taskQueue?.waitingForConfirmation ?? 0) > 0 || (agentChatActiveRuntime.taskQueue?.waitingForUserInput ?? 0) > 0 || (agentChatActiveRuntime.taskQueue?.paused ?? 0) > 0), ); const currentAgentChatActiveSessions = agentChatSessions.filter( (session) => session.archivedAt === null, ); const currentAgentChatArchivedSessions = agentChatSessions.filter( (session) => session.archivedAt !== null, ); const currentAgentChatLlmWarning = getCurrentAgentChatLlmWarning( currentAgentChatAgent, ); const currentAgentChatNeedsUserInput = agentRuntimeNeedsUserInput(agentChatRuntime); const currentAgentChatSteerRuntime = currentAgentChatAgent ? matchingAgentRuntimeForSteer( [agentChatRuntime, agentChatActiveRuntime], currentAgentChatAgent.id, agentChatSelectedSessionId, ) : null; const currentAgentChatWaiting = agentChatReplyPhase !== 'idle' || agentChatPendingRuntimeRun !== null; const currentAgentChatWaitingStatus = agentChatPendingRuntimeRun && agentChatRuntime?.runId === agentChatPendingRuntimeRun.runId ? agentRuntimeConversationStatus(agentChatRuntime) : agentChatStatus; const currentAgentChatWaitingDetail = agentChatPendingRuntimeRun ? '任务仍在运行,状态和最终回复会自动更新' : '请求仍在进行中,收到回复后会立即显示'; return { launcherAgentChatAgents, agentChatProjectPath, setAgentChatProjectPath, agentChatSelectedAgentId, setAgentChatSelectedAgentId, agentChatMessages, agentChatSessions, agentChatSelectedSessionId, agentChatActiveSessionId, agentChatLegacySessionMode, agentChatConversationPath, agentChatSessionStatus, agentChatInput, setAgentChatInput, agentChatInteractionMode, setAgentChatInteractionMode, agentChatRunSubmitMode, setAgentChatRunSubmitMode, agentChatLlmStatus, agentChatStatus, setAgentChatStatus, agentChatBusy, agentChatMessagesRef, agentChatBackgroundBusy, agentChatRuntime, agentChatRuntimeError, agentChatGoal, agentChatGoalError, agentChatGoalDialog, setAgentChatGoalDialog, agentChatGoalDialogError, agentChatResumeConfirmation, setAgentChatResumeConfirmation, agentChatLoadVersionRef, agentChatRuntimeResumeProjectPathRef, handleAgentChatMessagesScroll, loadAgentChatLlmStatus, cancelAgentChatRuntimeResume, confirmAgentChatRuntimeResume, closeAgentChatGoalDialog, handleOpenProjectSupervisorChatWindow, handleAgentChatPickProjectDirectory, loadAgentChatConversation, resetAgentChatSessionView, handleAgentChatSelectSession, handleAgentChatCreateSession, handleAgentChatForkSession, handleAgentChatArchiveSession, handleAgentChatSubmit, handleAgentChatStartBackgroundTask, openAgentChatGoalDialog, handleAgentChatGoalDialogSubmit, handleAgentChatGoalControl, handleAgentChatCompactContext, handleAgentChatCancelRuntimeTask, handleAgentChatRetryRuntimeTask, handleAgentChatConfirmRuntimeTask, handleAgentChatRejectRuntimeTask, handleAgentChatSubmitUserInput, currentAgentChatAgent, currentAgentChatSession, currentAgentChatSessionArchived, currentAgentChatGoalStatus, currentAgentChatGoalDialogMode, currentAgentChatSessionMutationBlocked, currentAgentChatActiveSessions, currentAgentChatArchivedSessions, currentAgentChatLlmWarning, currentAgentChatNeedsUserInput, currentAgentChatSteerRuntime, currentAgentChatWaiting, currentAgentChatWaitingStatus, currentAgentChatWaitingDetail, }; } export type DeveloperAgentPanelController = ReturnType< typeof useDeveloperAgentPanel >;