Files
Genarrative/apps/ai-game-creator-shell/src/features/app-shell/developerAgentControls.ts
T
AIGameCreator App be89296492 拆分 AI 游戏创作客户端大型模块
拆分 App 认证、壳层、运行配置与项目摘要模块
拆分 Tauri 项目能力与 Rust 测试领域模块
拆分界面测试与 Agent Runtime 真实 E2E 套件
补充源码扫描和客户端模块化文档约定
2026-07-21 22:53:29 +08:00

984 lines
31 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { type FormEvent } from 'react';
import { resolveTauriInvoke } from '../../app/tauri';
import type {
AgentChatPendingRuntimeRun,
AgentConversationSessionRecord,
AgentGoalMutationResult,
AgentRuntimeContextCompactionResult,
AgentRuntimeResult,
AgentRuntimeUserInputRequest,
LauncherAgentChatAgent,
LocalConversationResult,
TauriInvoke,
} from '../../app/types';
import {
agentGoalStatusIsPaused,
agentGoalStatusIsTerminal,
agentRuntimeCancelStatus,
agentRuntimeConversationStatus,
agentRuntimeStartStatus,
agentRuntimeStateFromResult,
createAgentChatRunId,
isAgentRuntimeTerminalState,
mergeAgentGoalRecordFromRuntime,
parseAgentGoalItems,
} from '../agent-runtime';
import type { DeveloperAgentState } from './useDeveloperAgentState';
type DeveloperAgentControlOptions = {
state: DeveloperAgentState;
validateAgentChatProjectPath: (projectPath?: string) => string | null;
selectedLauncherAgentChatAgent: (
agentId?: string,
) => LauncherAgentChatAgent | null;
selectedLauncherAgentChatSession: (
sessionId?: string | null,
) => AgentConversationSessionRecord | null;
agentChatSessionInvokeArgs: (sessionId: string | null) => {
sessionId?: string;
};
updateAgentChatSessionMessageCount: (
sessionId: string | null,
messageCount: number,
) => void;
getCurrentAgentChatLlmWarning: (
agent?: LauncherAgentChatAgent | null,
) => string | null;
setAgentChatPendingRuntimeRun: (
pendingRun: AgentChatPendingRuntimeRun | null,
) => void;
};
export function createDeveloperAgentControls({
state,
validateAgentChatProjectPath,
selectedLauncherAgentChatAgent,
selectedLauncherAgentChatSession,
agentChatSessionInvokeArgs,
updateAgentChatSessionMessageCount,
getCurrentAgentChatLlmWarning,
setAgentChatPendingRuntimeRun,
}: DeveloperAgentControlOptions) {
const {
agentChatMessages,
setAgentChatMessages,
agentChatSelectedSessionId,
setAgentChatConversationPath,
setAgentChatInteractionMode,
setAgentChatStatus,
agentChatBusy,
agentChatBackgroundBusy,
setAgentChatBackgroundBusy,
agentChatRuntime,
setAgentChatRuntime,
setAgentChatActiveRuntime,
setAgentChatRuntimeError,
agentChatGoal,
setAgentChatGoal,
agentChatGoalError,
setAgentChatGoalError,
agentChatGoalDialog,
setAgentChatGoalDialog,
setAgentChatGoalDialogError,
agentChatLoadVersionRef,
agentChatProjectPathRef,
agentChatSelectedAgentIdRef,
agentChatSelectedSessionIdRef,
agentChatActiveSessionIdRef,
agentChatPendingRuntimeRunRef,
} = state;
function applyAgentChatGoalMutationResult(result: AgentGoalMutationResult) {
const runtimeState = agentRuntimeStateFromResult(result.runtime);
setAgentChatGoal(result.goal);
setAgentChatGoalError('');
setAgentChatRuntime(runtimeState);
if (
agentChatActiveSessionIdRef.current === null ||
agentChatActiveSessionIdRef.current === result.goal.sessionId
) {
setAgentChatActiveRuntime(runtimeState);
}
setAgentChatRuntimeError('');
return runtimeState;
}
async function refreshAgentChatConversationAfterGoalMutation(
invoke: TauriInvoke,
projectPathForChat: string,
agentId: string,
sessionId: string,
saveVersion: number,
) {
const conversation = await invoke<LocalConversationResult>(
'read_local_conversation',
{
projectPath: projectPathForChat,
agentId,
sessionId,
},
);
if (
agentChatLoadVersionRef.current !== saveVersion ||
agentChatProjectPathRef.current.trim() !== projectPathForChat ||
agentChatSelectedAgentIdRef.current !== agentId ||
agentChatSelectedSessionIdRef.current !== sessionId
) {
return null;
}
setAgentChatMessages(conversation.messages);
setAgentChatConversationPath(conversation.path);
updateAgentChatSessionMessageCount(sessionId, conversation.messages.length);
return conversation;
}
function openAgentChatGoalDialog(mode: 'create' | 'edit') {
const projectPathForChat = validateAgentChatProjectPath();
const agent = selectedLauncherAgentChatAgent();
const session = selectedLauncherAgentChatSession();
const sessionId = agentChatSelectedSessionId;
if (
!projectPathForChat ||
!agent ||
!sessionId ||
agentChatBusy ||
agentChatBackgroundBusy
) {
if (!sessionId) {
setAgentChatStatus('请先读取并选择 Agent Session');
}
return;
}
if (session?.archivedAt !== null && session?.archivedAt !== undefined) {
setAgentChatStatus('已归档会话只能查看 Goal');
return;
}
if (agentChatGoalError) {
setAgentChatStatus(agentChatGoalError);
return;
}
if (mode === 'edit' && !agentChatGoal) {
setAgentChatStatus('当前 Session 没有可编辑的持久目标');
return;
}
if (
mode === 'create' &&
agentChatGoal &&
!agentGoalStatusIsTerminal(agentChatGoal.status)
) {
setAgentChatStatus('当前 Session 已有未结束的持久目标');
return;
}
const goal = mode === 'edit' ? agentChatGoal : null;
setAgentChatInteractionMode('goal');
setAgentChatGoalDialogError('');
setAgentChatGoalDialog({
mode,
projectPath: projectPathForChat,
agentId: agent.id,
sessionId,
goalId: goal?.goalId ?? null,
expectedRevision: goal?.revision ?? null,
outcome: goal?.outcome ?? '',
constraintsText: goal?.constraints.join('\n') ?? '',
verificationText: goal?.verification.join('\n') ?? '',
});
}
async function handleAgentChatGoalDialogSubmit(
event: FormEvent<HTMLFormElement>,
) {
event.preventDefault();
const dialog = agentChatGoalDialog;
if (!dialog || agentChatBusy || agentChatBackgroundBusy) {
return;
}
if (
agentChatProjectPathRef.current.trim() !== dialog.projectPath ||
agentChatSelectedAgentIdRef.current !== dialog.agentId ||
agentChatSelectedSessionIdRef.current !== dialog.sessionId
) {
setAgentChatGoalDialogError('Agent 或 Session 已变化,请关闭后重新打开');
return;
}
const outcome = dialog.outcome.trim();
const constraints = parseAgentGoalItems(dialog.constraintsText);
const verification = parseAgentGoalItems(dialog.verificationText);
if (!outcome) {
setAgentChatGoalDialogError('Outcome 不能为空');
return;
}
if ([...outcome].length > 4_000) {
setAgentChatGoalDialogError('Outcome 不能超过 4000 个字符');
return;
}
if (constraints.length > 8 || verification.length > 8) {
setAgentChatGoalDialogError('Constraints 和 Verification 各最多 8 条');
return;
}
if (
[...constraints, ...verification].some((item) => [...item].length > 1_000)
) {
setAgentChatGoalDialogError('每条约束或完成标准不能超过 1000 个字符');
return;
}
if (
new Set(constraints).size !== constraints.length ||
new Set(verification).size !== verification.length
) {
setAgentChatGoalDialogError('Constraints 或 Verification 不能重复');
return;
}
const llmWarning = getCurrentAgentChatLlmWarning(
selectedLauncherAgentChatAgent(dialog.agentId),
);
if (llmWarning) {
setAgentChatGoalDialogError(llmWarning);
return;
}
const invoke = resolveTauriInvoke();
if (!invoke) {
setAgentChatGoalDialogError('需要在 Tauri App 内运行');
return;
}
const saveVersion = agentChatLoadVersionRef.current + 1;
agentChatLoadVersionRef.current = saveVersion;
const previousPendingRun = agentChatPendingRuntimeRunRef.current;
setAgentChatBackgroundBusy(true);
setAgentChatGoalDialogError('');
setAgentChatStatus(
dialog.mode === 'create' ? '正在开始持久目标' : '正在更新持久目标',
);
try {
const result =
dialog.mode === 'create'
? await invoke<AgentGoalMutationResult>(
'start_game_creator_agent_goal',
{
projectPath: dialog.projectPath,
agentId: dialog.agentId,
sessionId: dialog.sessionId,
outcome,
constraints,
verification,
runId: createAgentChatRunId('launcher-agent-goal'),
},
)
: await invoke<AgentGoalMutationResult>(
'edit_game_creator_agent_goal',
{
projectPath: dialog.projectPath,
agentId: dialog.agentId,
sessionId: dialog.sessionId,
goalId: dialog.goalId,
expectedRevision: dialog.expectedRevision,
outcome,
constraints,
verification,
},
);
if (agentChatLoadVersionRef.current !== saveVersion) {
return;
}
if (
result.goal.agentId !== dialog.agentId ||
result.goal.sessionId !== dialog.sessionId ||
(dialog.mode === 'edit' && result.goal.goalId !== dialog.goalId)
) {
throw new Error('Goal command 返回了错误的 Agent 或 Session 身份');
}
const runtimeState = applyAgentChatGoalMutationResult(result);
const conversation = await refreshAgentChatConversationAfterGoalMutation(
invoke,
dialog.projectPath,
dialog.agentId,
dialog.sessionId,
saveVersion,
);
if (agentChatLoadVersionRef.current !== saveVersion) {
return;
}
if (agentGoalStatusIsPaused(result.goal.status)) {
if (
agentChatPendingRuntimeRunRef.current?.runId === result.goal.runId
) {
setAgentChatPendingRuntimeRun(null);
}
} else if (!agentGoalStatusIsTerminal(result.goal.status)) {
setAgentChatPendingRuntimeRun({
projectPath: dialog.projectPath,
agentId: dialog.agentId,
sessionId: dialog.sessionId,
runId: result.goal.runId || runtimeState.runId,
messageCount:
conversation?.messages.length ?? agentChatMessages.length,
});
}
setAgentChatGoalDialog(null);
setAgentChatGoalDialogError('');
setAgentChatStatus(
dialog.mode === 'create'
? `已开始持久目标:${result.goal.goalId}`
: `持久目标已更新到 Revision ${result.goal.revision}${
result.providerInterrupted ? 'Provider 已中断并重新规划' : ''
}`,
);
} catch (error) {
if (agentChatLoadVersionRef.current !== saveVersion) {
return;
}
setAgentChatPendingRuntimeRun(previousPendingRun);
const message = error instanceof Error ? error.message : String(error);
setAgentChatGoalDialogError(message);
setAgentChatStatus(`持久目标操作失败:${message}`);
} finally {
if (agentChatLoadVersionRef.current === saveVersion) {
setAgentChatBackgroundBusy(false);
}
}
}
async function handleAgentChatGoalControl(
operation: 'pause' | 'resume' | 'clear',
) {
const projectPathForChat = validateAgentChatProjectPath();
const agent = selectedLauncherAgentChatAgent();
const session = selectedLauncherAgentChatSession();
const sessionId = agentChatSelectedSessionId;
const goal = agentChatGoal;
if (
!projectPathForChat ||
!agent ||
!sessionId ||
!goal ||
agentChatBusy ||
agentChatBackgroundBusy
) {
return;
}
if (session?.archivedAt !== null && session?.archivedAt !== undefined) {
setAgentChatStatus('已归档会话只能查看 Goal');
return;
}
if (goal.agentId !== agent.id || goal.sessionId !== sessionId) {
setAgentChatStatus('当前 Goal 与 Agent Session 身份不匹配,请刷新');
return;
}
const invoke = resolveTauriInvoke();
if (!invoke) {
setAgentChatStatus('需要在 Tauri App 内运行');
return;
}
const saveVersion = agentChatLoadVersionRef.current + 1;
agentChatLoadVersionRef.current = saveVersion;
setAgentChatBackgroundBusy(true);
setAgentChatStatus(
operation === 'pause'
? '正在暂停持久目标'
: operation === 'resume'
? '正在恢复持久目标'
: '正在清理持久目标',
);
try {
const args = {
projectPath: projectPathForChat,
agentId: agent.id,
sessionId,
goalId: goal.goalId,
expectedRevision: goal.revision,
};
const result =
operation === 'pause'
? await invoke<AgentGoalMutationResult>(
'pause_game_creator_agent_goal',
args,
)
: operation === 'resume'
? await invoke<AgentGoalMutationResult>(
'resume_game_creator_agent_goal',
args,
)
: await invoke<AgentGoalMutationResult>(
'clear_game_creator_agent_goal',
args,
);
if (agentChatLoadVersionRef.current !== saveVersion) {
return;
}
if (
result.goal.goalId !== goal.goalId ||
result.goal.agentId !== agent.id ||
result.goal.sessionId !== sessionId
) {
throw new Error('Goal command 返回了错误的 Goal 身份');
}
applyAgentChatGoalMutationResult(result);
if (operation === 'pause') {
if (agentChatPendingRuntimeRunRef.current?.runId === goal.runId) {
setAgentChatPendingRuntimeRun(null);
}
} else if (operation === 'resume') {
setAgentChatPendingRuntimeRun({
projectPath: projectPathForChat,
agentId: agent.id,
sessionId,
runId: result.goal.runId,
messageCount: agentChatMessages.length,
});
} else if (result.goal.status === 'cleared') {
if (agentChatPendingRuntimeRunRef.current?.runId === goal.runId) {
setAgentChatPendingRuntimeRun(null);
}
} else {
setAgentChatPendingRuntimeRun({
projectPath: projectPathForChat,
agentId: agent.id,
sessionId,
runId: result.goal.runId,
messageCount: agentChatMessages.length,
});
}
setAgentChatStatus(
operation === 'pause'
? result.goal.status === 'paused'
? '持久目标已暂停'
: '持久目标正在安全边界暂停'
: operation === 'resume'
? '持久目标已恢复,同一 Run 正在继续'
: `持久目标清理状态:${result.goal.status}`,
);
} catch (error) {
if (agentChatLoadVersionRef.current !== saveVersion) {
return;
}
setAgentChatStatus(
`持久目标操作失败:${
error instanceof Error ? error.message : String(error)
}`,
);
} finally {
if (agentChatLoadVersionRef.current === saveVersion) {
setAgentChatBackgroundBusy(false);
}
}
}
async function handleAgentChatCompactContext() {
const projectPathForChat = validateAgentChatProjectPath();
const agent = selectedLauncherAgentChatAgent();
const selectedSession = selectedLauncherAgentChatSession();
if (
!projectPathForChat ||
!agent ||
!selectedSession ||
selectedSession.archivedAt
) {
setAgentChatStatus('请选择可写的 Agent Session');
return;
}
const invoke = resolveTauriInvoke();
if (!invoke) {
setAgentChatStatus('上下文压缩需要在 Tauri App 内运行');
return;
}
setAgentChatBackgroundBusy(true);
setAgentChatStatus('正在压缩当前 Agent Session 的旧上下文');
try {
const result = await invoke<AgentRuntimeContextCompactionResult>(
'compact_game_creator_agent_runtime_context',
{
projectPath: projectPathForChat,
agentId: agent.id,
sessionId: selectedSession.sessionId,
},
);
const runtime = await invoke<AgentRuntimeResult>(
'read_game_creator_agent_runtime',
{
projectPath: projectPathForChat,
agentId: agent.id,
sessionId: selectedSession.sessionId,
},
);
setAgentChatRuntime((current) =>
agentRuntimeStateFromResult(runtime, current),
);
setAgentChatStatus(
result.reused
? `上下文已是最新压缩 revision ${result.revision}`
: `上下文压缩完成:revision ${result.revision},预计 ${result.estimatedTokensBefore} -> ${result.estimatedTokensAfter} tokens`,
);
} catch (error) {
setAgentChatStatus(
`上下文压缩失败:${
error instanceof Error ? error.message : String(error)
}`,
);
} finally {
setAgentChatBackgroundBusy(false);
}
}
async function handleAgentChatCancelRuntimeTask(runId: string) {
const projectPathForChat = validateAgentChatProjectPath();
const agent = selectedLauncherAgentChatAgent();
const selectedSession = selectedLauncherAgentChatSession();
if (
!projectPathForChat ||
!agent ||
!runId ||
agentChatBusy ||
agentChatBackgroundBusy
) {
return;
}
if (selectedSession && selectedSession.archivedAt !== null) {
setAgentChatStatus('已归档会话只能查看 Runtime 历史');
return;
}
const invoke = resolveTauriInvoke();
if (!invoke) {
setAgentChatStatus('需要在 Tauri App 内运行');
return;
}
const saveVersion = agentChatLoadVersionRef.current + 1;
agentChatLoadVersionRef.current = saveVersion;
setAgentChatBackgroundBusy(true);
setAgentChatStatus('正在取消 Agent 后台任务');
try {
const runtime = await invoke<AgentRuntimeResult>(
'cancel_game_creator_agent_runtime_task',
{
projectPath: projectPathForChat,
agentId: agent.id,
runId,
},
);
if (agentChatLoadVersionRef.current !== saveVersion) {
return;
}
const runtimeState = agentRuntimeStateFromResult(runtime);
setAgentChatRuntime(runtimeState);
setAgentChatActiveRuntime(runtimeState);
setAgentChatGoal((current) =>
mergeAgentGoalRecordFromRuntime(current, runtimeState),
);
setAgentChatRuntimeError('');
setAgentChatStatus(agentRuntimeCancelStatus(runtimeState, runId));
} catch (error) {
if (agentChatLoadVersionRef.current !== saveVersion) {
return;
}
setAgentChatStatus(
error instanceof Error ? error.message : String(error),
);
} finally {
if (agentChatLoadVersionRef.current === saveVersion) {
setAgentChatBackgroundBusy(false);
}
}
}
async function handleAgentChatRetryRuntimeTask(runId: string) {
const projectPathForChat = validateAgentChatProjectPath();
const agent = selectedLauncherAgentChatAgent();
const selectedSession = selectedLauncherAgentChatSession();
const sessionIdForTask = agentChatSelectedSessionId;
if (
!projectPathForChat ||
!agent ||
!runId ||
agentChatBusy ||
agentChatBackgroundBusy
) {
return;
}
if (selectedSession && selectedSession.archivedAt !== null) {
setAgentChatStatus('已归档会话不能重试任务');
return;
}
if (
agentGoalStatusIsPaused(agentChatGoal?.status) ||
agentGoalStatusIsPaused(agentChatRuntime?.goalStatus) ||
agentGoalStatusIsPaused(agentChatRuntime?.status) ||
agentGoalStatusIsPaused(agentChatRuntime?.phase)
) {
setAgentChatStatus('持久目标已暂停,请先恢复 Goal');
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;
setAgentChatBackgroundBusy(true);
setAgentChatStatus('正在重试 Agent 后台任务');
try {
const runtime = await invoke<AgentRuntimeResult>(
'retry_game_creator_agent_runtime_task',
{
projectPath: projectPathForChat,
agentId: agent.id,
runId,
nextRunId: createAgentChatRunId('launcher-agent-retry'),
},
);
if (agentChatLoadVersionRef.current !== saveVersion) {
return;
}
const runtimeState = agentRuntimeStateFromResult(runtime);
setAgentChatRuntime(runtimeState);
setAgentChatActiveRuntime(runtimeState);
setAgentChatGoal((current) =>
mergeAgentGoalRecordFromRuntime(current, runtimeState),
);
setAgentChatRuntimeError('');
const conversation = await invoke<LocalConversationResult>(
'read_local_conversation',
{
projectPath: projectPathForChat,
agentId: agent.id,
...agentChatSessionInvokeArgs(sessionIdForTask),
},
);
if (agentChatLoadVersionRef.current !== saveVersion) {
return;
}
setAgentChatMessages(conversation.messages);
setAgentChatStatus(agentRuntimeStartStatus(runtime));
} catch (error) {
if (agentChatLoadVersionRef.current !== saveVersion) {
return;
}
setAgentChatStatus(
error instanceof Error ? error.message : String(error),
);
} finally {
if (agentChatLoadVersionRef.current === saveVersion) {
setAgentChatBackgroundBusy(false);
}
}
}
async function handleAgentChatConfirmRuntimeTask(
runId: string,
actionId: string,
) {
const projectPathForChat = validateAgentChatProjectPath();
const agent = selectedLauncherAgentChatAgent();
const selectedSession = selectedLauncherAgentChatSession();
const sessionIdForTask = agentChatSelectedSessionId;
if (
!projectPathForChat ||
!agent ||
!runId ||
!actionId ||
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;
setAgentChatBackgroundBusy(true);
setAgentChatStatus('正在确认并继续 Agent 后台任务');
try {
const runtime = await invoke<AgentRuntimeResult>(
'confirm_game_creator_agent_runtime_task',
{
projectPath: projectPathForChat,
agentId: agent.id,
runId,
actionId,
note: '开发者已确认待执行工具动作',
},
);
if (agentChatLoadVersionRef.current !== saveVersion) {
return;
}
const runtimeState = agentRuntimeStateFromResult(runtime);
setAgentChatRuntime(runtimeState);
setAgentChatActiveRuntime(runtimeState);
setAgentChatGoal((current) =>
mergeAgentGoalRecordFromRuntime(current, runtimeState),
);
setAgentChatRuntimeError('');
const conversation = await invoke<LocalConversationResult>(
'read_local_conversation',
{
projectPath: projectPathForChat,
agentId: agent.id,
...agentChatSessionInvokeArgs(sessionIdForTask),
},
);
if (agentChatLoadVersionRef.current !== saveVersion) {
return;
}
setAgentChatMessages(conversation.messages);
setAgentChatStatus(agentRuntimeStartStatus(runtime));
} catch (error) {
if (agentChatLoadVersionRef.current !== saveVersion) {
return;
}
setAgentChatStatus(
error instanceof Error ? error.message : String(error),
);
} finally {
if (agentChatLoadVersionRef.current === saveVersion) {
setAgentChatBackgroundBusy(false);
}
}
}
async function handleAgentChatRejectRuntimeTask(
runId: string,
actionId: string,
) {
const projectPathForChat = validateAgentChatProjectPath();
const agent = selectedLauncherAgentChatAgent();
const selectedSession = selectedLauncherAgentChatSession();
const sessionIdForTask = agentChatSelectedSessionId;
if (
!projectPathForChat ||
!agent ||
!runId ||
!actionId ||
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;
setAgentChatBackgroundBusy(true);
setAgentChatStatus('正在拒绝工具动作并继续 Agent 后台任务');
try {
const runtime = await invoke<AgentRuntimeResult>(
'reject_game_creator_agent_runtime_task',
{
projectPath: projectPathForChat,
agentId: agent.id,
runId,
actionId,
note: '开发者拒绝待执行工具动作',
},
);
if (agentChatLoadVersionRef.current !== saveVersion) {
return;
}
const runtimeState = agentRuntimeStateFromResult(runtime);
setAgentChatRuntime(runtimeState);
setAgentChatActiveRuntime(runtimeState);
setAgentChatGoal((current) =>
mergeAgentGoalRecordFromRuntime(current, runtimeState),
);
setAgentChatRuntimeError('');
const conversation = await invoke<LocalConversationResult>(
'read_local_conversation',
{
projectPath: projectPathForChat,
agentId: agent.id,
...agentChatSessionInvokeArgs(sessionIdForTask),
},
);
if (agentChatLoadVersionRef.current !== saveVersion) {
return;
}
setAgentChatMessages(conversation.messages);
setAgentChatStatus(agentRuntimeStartStatus(runtime));
} catch (error) {
if (agentChatLoadVersionRef.current !== saveVersion) {
return;
}
setAgentChatStatus(
error instanceof Error ? error.message : String(error),
);
} finally {
if (agentChatLoadVersionRef.current === saveVersion) {
setAgentChatBackgroundBusy(false);
}
}
}
async function handleAgentChatSubmitUserInput(
request: AgentRuntimeUserInputRequest,
responseId: string,
answers: Record<string, string>,
) {
const projectPathForChat = validateAgentChatProjectPath();
const agent = selectedLauncherAgentChatAgent();
const selectedSession = selectedLauncherAgentChatSession();
const sessionIdForTask = agentChatSelectedSessionId;
const currentRequest = agentChatRuntime?.userInputRequest;
if (
!projectPathForChat ||
!agent ||
!sessionIdForTask ||
!responseId ||
agentChatBusy ||
agentChatBackgroundBusy
) {
return;
}
if (selectedSession && selectedSession.archivedAt !== null) {
setAgentChatStatus('已归档会话不能回答 Agent 问题');
return;
}
if (
request.agentId !== agent.id ||
request.sessionId !== sessionIdForTask ||
request.runId !== agentChatRuntime?.runId ||
currentRequest?.requestId !== request.requestId ||
currentRequest?.actionId !== request.actionId
) {
setAgentChatRuntimeError('待回答请求已变更,请刷新 Runtime 状态');
return;
}
const invoke = resolveTauriInvoke();
if (!invoke) {
setAgentChatStatus('需要在 Tauri App 内运行');
return;
}
const saveVersion = agentChatLoadVersionRef.current + 1;
agentChatLoadVersionRef.current = saveVersion;
const previousPendingRun = agentChatPendingRuntimeRunRef.current;
setAgentChatPendingRuntimeRun({
projectPath: projectPathForChat,
agentId: agent.id,
sessionId: sessionIdForTask,
runId: request.runId,
messageCount: agentChatMessages.length,
});
setAgentChatBackgroundBusy(true);
setAgentChatRuntimeError('');
setAgentChatStatus('正在提交回答并继续当前 Run');
try {
const result = await invoke<AgentRuntimeResult>(
'answer_game_creator_agent_runtime_user_input',
{
projectPath: projectPathForChat,
agentId: agent.id,
runId: request.runId,
actionId: request.actionId,
requestId: request.requestId,
responseId,
answers,
},
);
if (agentChatLoadVersionRef.current !== saveVersion) {
return;
}
const runtimeState = agentRuntimeStateFromResult(
result,
agentChatRuntime,
);
if (
runtimeState.agentId !== agent.id ||
runtimeState.sessionId !== sessionIdForTask ||
runtimeState.runId !== request.runId
) {
throw new Error('Agent 回答后 Runtime 身份不匹配');
}
setAgentChatRuntime(runtimeState);
if (
agentChatActiveSessionIdRef.current === null ||
agentChatActiveSessionIdRef.current === sessionIdForTask
) {
setAgentChatActiveRuntime(runtimeState);
}
setAgentChatGoal((current) =>
mergeAgentGoalRecordFromRuntime(current, runtimeState),
);
const conversation = await invoke<LocalConversationResult>(
'read_local_conversation',
{
projectPath: projectPathForChat,
agentId: agent.id,
sessionId: sessionIdForTask,
},
);
if (agentChatLoadVersionRef.current !== saveVersion) {
return;
}
setAgentChatMessages(conversation.messages);
setAgentChatConversationPath(conversation.path);
updateAgentChatSessionMessageCount(
sessionIdForTask,
conversation.messages.length,
);
setAgentChatPendingRuntimeRun(
isAgentRuntimeTerminalState(runtimeState)
? null
: {
projectPath: projectPathForChat,
agentId: agent.id,
sessionId: sessionIdForTask,
runId: request.runId,
messageCount: conversation.messages.length,
},
);
setAgentChatRuntimeError('');
setAgentChatStatus(agentRuntimeConversationStatus(runtimeState));
} catch (error) {
if (agentChatLoadVersionRef.current !== saveVersion) {
return;
}
setAgentChatPendingRuntimeRun(previousPendingRun);
const message = error instanceof Error ? error.message : String(error);
setAgentChatRuntimeError(message);
setAgentChatStatus(`回答提交失败:${message}`);
} finally {
if (agentChatLoadVersionRef.current === saveVersion) {
setAgentChatBackgroundBusy(false);
}
}
}
return {
openAgentChatGoalDialog,
handleAgentChatGoalDialogSubmit,
handleAgentChatGoalControl,
handleAgentChatCompactContext,
handleAgentChatCancelRuntimeTask,
handleAgentChatRetryRuntimeTask,
handleAgentChatConfirmRuntimeTask,
handleAgentChatRejectRuntimeTask,
handleAgentChatSubmitUserInput,
};
}