c2354f855f
新增 llm_router_account 表、procedures 与客户端绑定 api-server 改为 New API 管理员流程签发独立 Router 账号 Router 成功后执行幂等后置泥点扣费 生产外锁定非生产 Router 控制面为 loopback AGC 正式模式清除手工 Provider 凭据并锁定官方代理 AGC 状态面只返回账号凭据与官方路由安全元数据 后台数据库表查询适配账号凭据状态展示 补充 AGC 与后端测试、环境示例和架构文档 修正 dev-stack 状态路径按仓库脚本位置解析
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 ? ',智能服务已中断并重新规划' : ''
|
|
}`,
|
|
);
|
|
} 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,
|
|
};
|
|
}
|