be89296492
拆分 App 认证、壳层、运行配置与项目摘要模块 拆分 Tauri 项目能力与 Rust 测试领域模块 拆分界面测试与 Agent Runtime 真实 E2E 套件 补充源码扫描和客户端模块化文档约定
984 lines
31 KiB
TypeScript
984 lines
31 KiB
TypeScript
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,
|
||
};
|
||
}
|