68dfd2c24f
增加 DirectProject 流式可见文本投影和敏感内容过滤 接入受控 web search 工具及参数、网络边界测试 补充配置迁移和运行时回归覆盖
1908 lines
62 KiB
TypeScript
1908 lines
62 KiB
TypeScript
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<LocalConversationResult>(
|
||
'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<void>((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<AgentGoalRecord | null>(
|
||
'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<HTMLDivElement>) {
|
||
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<GameCreatorAgentRuntimeUpdateEvent>(
|
||
'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<AgentRuntimeResult>(
|
||
'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<GameCreatorLlmConfigStatus>(
|
||
'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<AgentRuntimeResult[]>(
|
||
'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<AgentRuntimeResult[]>(
|
||
'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<string | null>(
|
||
'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<AgentConversationSessionListResult>(
|
||
'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<AgentGoalRecord | null>(
|
||
'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<LocalConversationResult>(
|
||
'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<AgentRuntimeResult>(
|
||
'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<AgentConversationSessionListResult>(
|
||
'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<AgentConversationSessionListResult>(
|
||
'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<AgentConversationSessionListResult>(
|
||
'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<AgentConversationSessionListResult>(
|
||
'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<HTMLFormElement>) {
|
||
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<LocalConversationResult>(
|
||
'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<GameCreatorRoleAgentChatStreamEvent>(
|
||
'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<GameCreatorChatAgentReply>(
|
||
'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<GameCreatorChatAgentReply>(
|
||
'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<AgentRuntimeResult>(
|
||
'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<LocalConversationResult>(
|
||
'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<LocalConversationResult>(
|
||
'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<AgentRuntimeSteerResult>(
|
||
'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<AgentRuntimeResult>(
|
||
'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<LocalConversationResult>(
|
||
'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
|
||
>;
|