接入策划顾问态做成游戏运行时切换
新增项目 Agent 运行时模式持久化与恢复判断 在顾问态增加做成游戏按钮并切换同项目 DirectProject 切换时不自动发起首轮 Provider 请求
This commit is contained in:
@@ -813,6 +813,12 @@ pub(crate) fn hydrate_design_agent_session(
|
||||
) -> Result<Option<DesignView>, String> {
|
||||
let root = Path::new(project_path.trim());
|
||||
enforce_project_permission_policy(root, "conversation.read")?;
|
||||
if read_design_runtime_mode(root)?
|
||||
.as_ref()
|
||||
.is_some_and(|mode| mode.active_runtime == "game")
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
let project_id = design_project_id(root)?;
|
||||
let Some(session) = read_design_session(root)? else {
|
||||
return Ok(None);
|
||||
@@ -824,6 +830,21 @@ pub(crate) fn hydrate_design_agent_session(
|
||||
Ok(Some(design_view(&session, active.is_none())))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub(crate) fn set_design_agent_runtime_mode(
|
||||
project_path: String,
|
||||
active_runtime: String,
|
||||
) -> Result<DesignRuntimeMode, String> {
|
||||
let root = Path::new(project_path.trim());
|
||||
enforce_project_permission_policy(root, "conversation.write")?;
|
||||
design_project_id(root)?;
|
||||
let _lock = acquire_game_creator_agent_runtime_project_write_lock_with_wait(
|
||||
root,
|
||||
"design.runtime-mode",
|
||||
)?;
|
||||
write_design_runtime_mode(root, active_runtime.trim())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub(crate) async fn continue_design_agent_session(
|
||||
app: tauri::AppHandle,
|
||||
|
||||
@@ -9,8 +9,44 @@ use uuid::Uuid;
|
||||
pub(crate) const DESIGN_SESSION_SCHEMA_VERSION: &str = "design-agent-session.v1";
|
||||
pub(crate) const DESIGN_SESSION_ENGINE: &str = "design-agent";
|
||||
pub(crate) const DESIGN_SESSION_PATH: &str = ".agent/design-agent/session.json";
|
||||
pub(crate) const DESIGN_RUNTIME_MODE_PATH: &str = ".agent/runtime-mode.json";
|
||||
const DESIGN_SESSION_MAX_BYTES: usize = 64 * 1024 * 1024;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct DesignRuntimeMode {
|
||||
pub(crate) active_runtime: String,
|
||||
}
|
||||
|
||||
pub(crate) fn read_design_runtime_mode(root: &Path) -> Result<Option<DesignRuntimeMode>, String> {
|
||||
read_agent_runtime_json_sidecar_with_max_bytes(
|
||||
root,
|
||||
DESIGN_RUNTIME_MODE_PATH,
|
||||
"项目 Agent 运行时模式",
|
||||
4096,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn write_design_runtime_mode(
|
||||
root: &Path,
|
||||
active_runtime: &str,
|
||||
) -> Result<DesignRuntimeMode, String> {
|
||||
if !matches!(active_runtime, "design" | "game") {
|
||||
return Err("未知项目 Agent 运行时模式".to_string());
|
||||
}
|
||||
let mode = DesignRuntimeMode {
|
||||
active_runtime: active_runtime.to_string(),
|
||||
};
|
||||
write_agent_runtime_json_sidecar_with_max_bytes(
|
||||
root,
|
||||
DESIGN_RUNTIME_MODE_PATH,
|
||||
"项目 Agent 运行时模式",
|
||||
&mode,
|
||||
4096,
|
||||
)?;
|
||||
Ok(mode)
|
||||
}
|
||||
|
||||
pub(crate) const DESIGN_PHASES: [&str; 6] = [
|
||||
"concept",
|
||||
"top_design",
|
||||
|
||||
@@ -2583,6 +2583,7 @@ fn main() {
|
||||
decide_planning_artifact_v2,
|
||||
hydrate_planning_session_v2,
|
||||
hydrate_design_agent_session,
|
||||
set_design_agent_runtime_mode,
|
||||
continue_design_agent_session,
|
||||
decide_design_phase,
|
||||
list_design_workspace,
|
||||
|
||||
@@ -526,6 +526,7 @@ type AppProps = {
|
||||
) => void;
|
||||
onAgentResultsChange?: (results: ProjectAgentResultSummary[]) => void;
|
||||
onMakeGameFromApprovedGdd?: (projectPath: string) => Promise<void>;
|
||||
onSwitchToGameRuntime?: (projectPath: string) => Promise<void>;
|
||||
};
|
||||
|
||||
type ExecuteChatAgentReplyInput = {
|
||||
@@ -554,6 +555,7 @@ export function App({
|
||||
onAgentRuntimeSummariesChange,
|
||||
onAgentResultsChange,
|
||||
onMakeGameFromApprovedGdd,
|
||||
onSwitchToGameRuntime,
|
||||
}: AppProps = {}) {
|
||||
const { setTitle: setWindowTitle } = useWindowChrome();
|
||||
const [planningV2Active, setPlanningV2Active] = useState(planningStartMode);
|
||||
@@ -11721,6 +11723,15 @@ export function App({
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
onDesignMakeGame={
|
||||
useDesignAgentSurface && onSwitchToGameRuntime
|
||||
? () => {
|
||||
const nextProjectPath = resolveChatProjectPath(localProject);
|
||||
if (nextProjectPath)
|
||||
void onSwitchToGameRuntime(nextProjectPath);
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
onDesignOpenFile={
|
||||
useDesignAgentSurface
|
||||
? (path) => {
|
||||
|
||||
@@ -58,6 +58,9 @@ export function WorkspaceLauncherShell({
|
||||
title: string;
|
||||
message: string;
|
||||
} | null>(null);
|
||||
const [agentRuntimeMode, setAgentRuntimeMode] = useState<'design' | 'game'>(
|
||||
'game',
|
||||
);
|
||||
const developerAgent = useDeveloperAgentPanel(launcherView);
|
||||
const homeProject = useHomeProjectCreation({
|
||||
setStatus,
|
||||
@@ -81,6 +84,11 @@ export function WorkspaceLauncherShell({
|
||||
createHomeDraftAutomatically,
|
||||
openProject,
|
||||
} = homeProject;
|
||||
useEffect(() => {
|
||||
setAgentRuntimeMode(
|
||||
currentProjectContext?.startMode === 'planning' ? 'design' : 'game',
|
||||
);
|
||||
}, [currentProjectContext?.projectPath, currentProjectContext?.startMode]);
|
||||
const activeProjectContextRef = useRef(currentProjectContext);
|
||||
const manifestMergeRef = useRef<ProjectManifestMergeState | null>(null);
|
||||
activeProjectContextRef.current = currentProjectContext;
|
||||
@@ -238,6 +246,16 @@ export function WorkspaceLauncherShell({
|
||||
});
|
||||
}
|
||||
|
||||
async function switchToGameRuntime(nextProjectPath: string) {
|
||||
const invoke = resolveTauriInvoke();
|
||||
if (!invoke) throw new Error('需要在陶泥儿客户端内运行');
|
||||
await invoke('set_design_agent_runtime_mode', {
|
||||
projectPath: nextProjectPath,
|
||||
activeRuntime: 'game',
|
||||
});
|
||||
setAgentRuntimeMode('game');
|
||||
}
|
||||
|
||||
const currentHelpTitle =
|
||||
launcherView === 'guide'
|
||||
? '使用指南'
|
||||
@@ -328,7 +346,10 @@ export function WorkspaceLauncherShell({
|
||||
preview={activeProjectPreview}
|
||||
agentRuntimeSummaries={activeProjectAgentRuntimeSummaries}
|
||||
agentResults={activeProjectAgentResults}
|
||||
planningStartMode={currentProjectContext.startMode === 'planning'}
|
||||
planningStartMode={
|
||||
agentRuntimeMode === 'design' &&
|
||||
currentProjectContext.startMode === 'planning'
|
||||
}
|
||||
onPlay={() =>
|
||||
requestCurrentProjectPlay(currentProjectContext.projectPath)
|
||||
}
|
||||
@@ -337,13 +358,25 @@ export function WorkspaceLauncherShell({
|
||||
onProjectsOpen={() => setLauncherView('projects')}
|
||||
supervisor={
|
||||
<ProjectSupervisor
|
||||
key={currentProjectContext.projectPath}
|
||||
key={`${currentProjectContext.projectPath}:${agentRuntimeMode}`}
|
||||
initialProjectPath={currentProjectContext.projectPath}
|
||||
initialProjectManifest={currentProjectContext.manifest}
|
||||
initialProjectKind={currentProjectContext.projectKind}
|
||||
initialSupervisorMessage={currentProjectContext.initialPrompt}
|
||||
initialCreationType={currentProjectContext.creationType}
|
||||
initialAttachments={currentProjectContext.attachments}
|
||||
initialSupervisorMessage={
|
||||
agentRuntimeMode === 'design'
|
||||
? currentProjectContext.initialPrompt
|
||||
: ''
|
||||
}
|
||||
initialCreationType={
|
||||
agentRuntimeMode === 'design'
|
||||
? currentProjectContext.creationType
|
||||
: null
|
||||
}
|
||||
initialAttachments={
|
||||
agentRuntimeMode === 'design'
|
||||
? currentProjectContext.attachments
|
||||
: []
|
||||
}
|
||||
orchestrationMode="single-supervisor"
|
||||
projectSupervisorOnly
|
||||
planningStartMode={
|
||||
@@ -358,6 +391,7 @@ export function WorkspaceLauncherShell({
|
||||
}
|
||||
onAgentResultsChange={setActiveProjectAgentResults}
|
||||
onMakeGameFromApprovedGdd={startGameFromApprovedGdd}
|
||||
onSwitchToGameRuntime={switchToGameRuntime}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -56,6 +56,7 @@ export type ProjectSupervisorComponentProps = {
|
||||
) => void;
|
||||
onAgentResultsChange?: (results: ProjectAgentResultSummary[]) => void;
|
||||
onMakeGameFromApprovedGdd?: (projectPath: string) => Promise<void>;
|
||||
onSwitchToGameRuntime?: (projectPath: string) => Promise<void>;
|
||||
};
|
||||
|
||||
export type WorkspaceLauncherShellProps = WorkspaceLauncherProps & {
|
||||
|
||||
@@ -30,6 +30,7 @@ type DesignAgentSurfaceProps = {
|
||||
text: string,
|
||||
) => void;
|
||||
onRetry: () => void;
|
||||
onMakeGame: () => void;
|
||||
onOpenFile: (path: string) => void;
|
||||
onClosePreview: () => void;
|
||||
};
|
||||
@@ -41,6 +42,7 @@ export function DesignAgentSurface({
|
||||
onApprove,
|
||||
onClarify,
|
||||
onRetry,
|
||||
onMakeGame,
|
||||
}: DesignAgentSurfaceProps) {
|
||||
const [clarifyText, setClarifyText] = useState('');
|
||||
const phase = view?.session.currentPhase ?? 'concept';
|
||||
@@ -54,7 +56,19 @@ export function DesignAgentSurface({
|
||||
<div className="design-agent-controls__header">
|
||||
<div>
|
||||
<span>当前阶段</span>
|
||||
<strong>{PHASE_LABELS[phase] ?? phase}</strong>
|
||||
<div className="design-agent-controls__phase-title">
|
||||
<strong>{PHASE_LABELS[phase] ?? phase}</strong>
|
||||
{phase === 'consultant' ? (
|
||||
<button
|
||||
type="button"
|
||||
className="design-agent-make-game"
|
||||
disabled={busy || Boolean(view?.running)}
|
||||
onClick={onMakeGame}
|
||||
>
|
||||
做成游戏
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
{view?.running ? (
|
||||
<span className="design-agent-controls__status">工作中</span>
|
||||
|
||||
@@ -115,6 +115,7 @@ type ProjectSupervisorViewProps = RuntimePanelProps & {
|
||||
text: string,
|
||||
) => void;
|
||||
onDesignRetry?: () => void;
|
||||
onDesignMakeGame?: () => void;
|
||||
onDesignOpenFile?: (path: string) => void;
|
||||
onDesignClosePreview?: () => void;
|
||||
};
|
||||
@@ -159,6 +160,7 @@ export function ProjectSupervisorView({
|
||||
onDesignApprove,
|
||||
onDesignClarify,
|
||||
onDesignRetry,
|
||||
onDesignMakeGame,
|
||||
onDesignOpenFile,
|
||||
onDesignClosePreview,
|
||||
...runtimePanelProps
|
||||
@@ -200,6 +202,7 @@ export function ProjectSupervisorView({
|
||||
onApprove={onDesignApprove ?? (() => undefined)}
|
||||
onClarify={onDesignClarify ?? (() => undefined)}
|
||||
onRetry={onDesignRetry ?? (() => undefined)}
|
||||
onMakeGame={onDesignMakeGame ?? (() => undefined)}
|
||||
onOpenFile={onDesignOpenFile ?? (() => undefined)}
|
||||
onClosePreview={onDesignClosePreview ?? (() => undefined)}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user