diff --git a/apps/ai-game-creator-shell/scripts/deterministic-lane-defense-provider.mjs b/apps/ai-game-creator-shell/scripts/deterministic-lane-defense-provider.mjs
index 2a3f86b18..98bf22ef3 100644
--- a/apps/ai-game-creator-shell/scripts/deterministic-lane-defense-provider.mjs
+++ b/apps/ai-game-creator-shell/scripts/deterministic-lane-defense-provider.mjs
@@ -120,7 +120,7 @@ export function deterministicLaneDefenseInitialHtml() {
*{box-sizing:border-box}body{margin:0;min-height:100vh;background:#f4f8ee;color:#18351f;font:16px system-ui,sans-serif}main{width:min(960px,100%);margin:auto;padding:18px}h1{margin:0 0 4px;font-size:clamp(28px,7vw,46px)}p{margin:4px 0 14px}.toolbar,.plants{display:flex;flex-wrap:wrap;gap:8px;margin:10px 0}button{min-height:44px;border:1px solid #315d35;background:#fff;color:#18351f;padding:9px 14px;font:inherit;font-weight:700;cursor:pointer}button:hover{background:#e6f3dc}.board{display:grid;gap:10px;background:#d9edc8;border:2px solid #315d35;padding:10px}.lane{display:grid;grid-template-columns:repeat(5,1fr);gap:6px}.cell{min-height:54px;background:#eef8e7}.status{font-weight:700;min-height:24px}#game{display:none;width:100%;height:auto;aspect-ratio:20/9;background:#18351f;border:2px solid #315d35}@media(max-width:520px){main{padding:12px}button{flex:1 1 44%}.cell{min-height:44px}}
-
+
灵露花园
GENARRATIVE_REAL_E2E_VISIBLE
Goal: defend the garden and win every wave.
diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/autonomous_policy.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/autonomous_policy.rs
index eaee84273..02afd4148 100644
--- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/autonomous_policy.rs
+++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/autonomous_policy.rs
@@ -560,7 +560,32 @@ pub(in crate::agent) fn autonomous_manifest_dag_in_progress_at(
root: &Path,
) -> Result {
let manifest = read_manifest_for_project(root)?;
- let seed_task_ids = new_game_creation_app_seed_tasks()
+ let source = read_game_creator_agent_runtime_at(root, GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID)
+ .ok()
+ .filter(|runtime| {
+ runtime.state.run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD
+ && agent_runtime_supervisor_source_is_trusted(&runtime.state.source)
+ })
+ .map(|runtime| runtime.state.source)
+ .or_else(|| {
+ let path = game_creator_agent_runtime_task_path(
+ root,
+ GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
+ );
+ read_all_game_creator_agent_runtime_tasks(&path)
+ .ok()
+ .map(latest_game_creator_agent_runtime_tasks)
+ .and_then(|records| {
+ records.into_iter().rev().find_map(|record| {
+ (record.run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD
+ && record.parent_run_id.is_none()
+ && agent_runtime_supervisor_source_is_trusted(&record.source))
+ .then_some(record.source)
+ })
+ })
+ })
+ .unwrap_or_else(|| AGENT_RUNTIME_SUPERVISOR_GUI_SOURCE.to_string());
+ let seed_task_ids = autonomous_manifest_seed_tasks_for_source(&source)
.into_iter()
.map(|task| task.id)
.collect::>();
diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver.rs
index fa86b1a46..791aca0bb 100644
--- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver.rs
+++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver.rs
@@ -100,6 +100,16 @@ pub(crate) const AGENT_RUNTIME_ISOLATED_CHILD_SOURCE: &str = "agent-isolated-chi
pub(crate) const AGENT_RUNTIME_ISOLATED_JOIN_SOURCE: &str = "agent-isolated-join";
pub(crate) const AGENT_RUNTIME_SUPERVISOR_GUI_SOURCE: &str = "project-supervisor-gui";
pub(crate) const AGENT_RUNTIME_SUPERVISOR_CLI_SOURCE: &str = "project-supervisor-cli";
+pub(crate) const AGENT_RUNTIME_SUPERVISOR_GAME_CHAT_SOURCE: &str = "project-supervisor-game-chat";
+
+pub(crate) fn agent_runtime_supervisor_source_is_trusted(source: &str) -> bool {
+ matches!(
+ source.trim(),
+ AGENT_RUNTIME_SUPERVISOR_GUI_SOURCE
+ | AGENT_RUNTIME_SUPERVISOR_CLI_SOURCE
+ | AGENT_RUNTIME_SUPERVISOR_GAME_CHAT_SOURCE
+ )
+}
pub(super) const AGENT_RUNTIME_RUN_PROFILE_BINDING_SCHEMA_VERSION: &str =
"game-creator-run-profile-binding.v1";
pub(super) const AGENT_RUNTIME_AUTONOMOUS_COMPLETION_CONTRACT_SCHEMA_VERSION: &str =
diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/lifecycle_control.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/lifecycle_control.rs
index 2770eb4de..512271191 100644
--- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/lifecycle_control.rs
+++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/lifecycle_control.rs
@@ -761,10 +761,7 @@ pub(crate) fn resolve_game_creator_agent_runtime_retry_configuration_at(
if binding.parent_run_id.is_some()
|| binding.root_agent_id != GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID
|| binding.root_run_id != task.run_id
- || !matches!(
- binding.source.as_str(),
- AGENT_RUNTIME_SUPERVISOR_GUI_SOURCE | AGENT_RUNTIME_SUPERVISOR_CLI_SOURCE
- )
+ || !agent_runtime_supervisor_source_is_trusted(&binding.source)
{
return Err("自主构建 Agent Runtime 重试绑定不是可信 Supervisor 根 Run".to_string());
}
diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/task_start.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/task_start.rs
index 5b3bc2f85..d50056898 100644
--- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/task_start.rs
+++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/task_start.rs
@@ -119,10 +119,7 @@ pub(crate) fn start_game_creator_supervisor_background_task_for_session_at(
source: &str,
run_profile: &str,
) -> Result {
- if !matches!(
- source,
- AGENT_RUNTIME_SUPERVISOR_GUI_SOURCE | AGENT_RUNTIME_SUPERVISOR_CLI_SOURCE
- ) {
+ if !agent_runtime_supervisor_source_is_trusted(source) {
return Err("Project Supervisor 提交 source 不受信任".to_string());
}
normalize_agent_runtime_run_profile(Some(run_profile))?;
@@ -641,10 +638,7 @@ fn current_autonomous_game_build_root_task_at(
&& record.run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD
&& record.parent_agent_id.is_none()
&& record.parent_run_id.is_none()
- && matches!(
- record.source.as_str(),
- AGENT_RUNTIME_SUPERVISOR_GUI_SOURCE | AGENT_RUNTIME_SUPERVISOR_CLI_SOURCE
- )
+ && agent_runtime_supervisor_source_is_trusted(&record.source)
&& seen_run_ids.insert(record.run_id.clone())
{
root_run_ids.push(record.run_id.clone());
@@ -658,8 +652,25 @@ fn current_autonomous_game_build_root_task_at(
.find(|record| record.run_id == *current_run_id))
}
-fn autonomous_manifest_ready_task_ids(tasks: &[GameCreationAppTaskState]) -> Vec {
- new_game_creation_app_seed_tasks()
+pub(in crate::agent) fn autonomous_manifest_seed_tasks_for_source(
+ source: &str,
+) -> Vec {
+ let seed_tasks = new_game_creation_app_seed_tasks();
+ if source.trim() == AGENT_RUNTIME_SUPERVISOR_GAME_CHAT_SOURCE {
+ seed_tasks
+ .into_iter()
+ .take_while(|task| task.id != "publish-strategy")
+ .collect()
+ } else {
+ seed_tasks
+ }
+}
+
+fn autonomous_manifest_ready_task_ids(
+ tasks: &[GameCreationAppTaskState],
+ source: &str,
+) -> Vec {
+ autonomous_manifest_seed_tasks_for_source(source)
.into_iter()
.filter_map(|seed_task| {
let task = tasks.iter().find(|task| task.id == seed_task.id)?;
@@ -893,9 +904,17 @@ pub(crate) fn schedule_autonomous_game_build_ready_tasks_at(
.count();
let available = 3usize.saturating_sub(active_count);
let seed_task_order = new_game_creation_app_seed_tasks();
+ let allowed_seed_task_ids =
+ autonomous_manifest_seed_tasks_for_source(&parent_binding.source)
+ .into_iter()
+ .map(|task| task.id)
+ .collect::>();
let mut candidates = Vec::new();
for seed_task in &seed_task_order {
+ if !allowed_seed_task_ids.contains(&seed_task.id) {
+ continue;
+ }
let Some(task) = manifest.tasks.iter().find(|task| task.id == seed_task.id) else {
continue;
};
@@ -917,7 +936,7 @@ pub(crate) fn schedule_autonomous_game_build_ready_tasks_at(
candidates.push((task.clone(), false));
}
}
- for task_id in autonomous_manifest_ready_task_ids(&manifest.tasks)
+ for task_id in autonomous_manifest_ready_task_ids(&manifest.tasks, &parent_binding.source)
.into_iter()
.take(limit.min(available))
{
@@ -1183,9 +1202,11 @@ pub(in crate::agent) fn project_autonomous_manifest_ready_task_terminal_at_locke
}),
)?;
let completed = status == GameCreationAppTaskStatus::Completed;
+ let game_chat_single_round =
+ root_parent_binding.source == AGENT_RUNTIME_SUPERVISOR_GAME_CHAT_SOURCE;
let root = root.to_path_buf();
tauri::async_runtime::spawn(async move {
- if completed {
+ if completed && !game_chat_single_round {
if let Err(error) = schedule_autonomous_game_build_ready_tasks_at(
&root,
&parent_agent_id,
@@ -1242,9 +1263,17 @@ pub(in crate::agent) fn render_autonomous_manifest_ready_task_background_prompt(
} else {
""
};
- return format!(
+ let code_visual_asset_requirement = if task.id == "code-prototype"
+ && editor_api_key_is_configured()
+ {
+ " External Editor API 已配置时,必须先调用 asset.list 确认 assets/art-spritesheet.png 已登记且 source.kind=canvas、资源有效;game/index.html 必须实际通过 HTML、CSS background 或 Canvas drawImage 引用 assets/art-spritesheet.png 作为游戏 UI 素材,不得只用 emoji、色块、CSS 绘图或占位文本冒充。"
+ } else {
+ ""
+ };
+ let owner_prompt = format!(
"{base}\n\n这是 autonomous-game-build 的正式 owner 写入任务。必须实际生成并写入非空正式产物:{paths};JSON 文件必须是可解析 JSON,code-prototype 的 game/index.html 不能沿用初始化占位。{publish_package_requirement}任务声明中的视觉图片继续按现有 visual gate 生成、登记并验收。完成修改后按当前 run 的验证门完成验证并直接交付结论;不要调用 task.update,Runtime 会在子 Run 终态后幂等投影 manifest。"
);
+ return format!("{owner_prompt}{code_visual_asset_requirement}");
}
format!(
"{base}\n\n这是 autonomous-game-build 的只读协调任务,不要修改项目文件,也不要为了 manifest 内部回执路径写入 memory/、game/、assets/ 或 exports/。只读取当前项目事实,完成方向协调、审查或验收并直接交付结论;不要调用 task.update,Runtime 会在子 Run 终态后幂等投影 manifest。"
diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion.rs
index 584d8daa6..9343793d6 100644
--- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion.rs
+++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion.rs
@@ -6,6 +6,7 @@ pub(crate) fn autonomous_game_build_root_run_active_at(root: &Path) -> bool {
if read_game_creator_agent_runtime_at(root, GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID).is_ok_and(
|runtime| {
runtime.state.run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD
+ && agent_runtime_supervisor_source_is_trusted(&runtime.state.source)
&& !matches!(
runtime.state.phase.as_str(),
"completed" | "failed" | "cancelled" | "budget-exhausted"
@@ -22,10 +23,7 @@ pub(crate) fn autonomous_game_build_root_run_active_at(root: &Path) -> bool {
tasks.into_iter().any(|task| {
task.run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD
&& task.parent_run_id.is_none()
- && matches!(
- task.source.as_str(),
- AGENT_RUNTIME_SUPERVISOR_GUI_SOURCE | AGENT_RUNTIME_SUPERVISOR_CLI_SOURCE
- )
+ && agent_runtime_supervisor_source_is_trusted(&task.source)
&& matches!(
task.status.as_str(),
"pending"
@@ -383,6 +381,42 @@ fn autonomous_manifest_owner_artifact_gaps_at(
Ok(gaps)
}
+fn autonomous_code_prototype_art_asset_reference_gap_at(
+ root: &Path,
+ task_id: &str,
+) -> Result