From 133cd1fa3bd534d6f8eaaf57d117d3ff3ec69b7c Mon Sep 17 00:00:00 2001 From: Linghong Date: Tue, 11 Aug 2026 13:43:35 +0000 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9A=E9=9D=9E=E5=A7=94?= =?UTF-8?q?=E6=B4=BE=E7=BE=8E=E6=9C=AF=E6=9D=A5=E6=BA=90=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E8=AF=AF=E5=85=A5=20game-chat=20=E8=B0=B1=E7=B3=BB=E9=97=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit M0A-2 将美术写入边界统一到严格谱系判定后,缺失 Run Profile 绑定从 "视为普通美术 run"(master 语义)变成 Err 失败关闭,且发生在只读工具 豁免之前:scheduler 直启与后台来源的 art run 全部工具被拦,恢复扫描 重执行 canvas.asset_generate 也因此永远发不出外部轮询请求。 在 retry 谱系判定之后、严格谓词之前增加 source 预筛:非 agent-delegate 来源直接判 Unrestricted,交由 autonomous owner 与路径策略管辖; agent-delegate 缺绑定与 agent-delegate-retry 的失败关闭语义不变。 治愈 CI 回归 recovery_scan_resumes_accepted_generation_on_default_worker_stack 与 command_output_read_allows_same_agent_history_and_rejects_cross_agent_action_id, 新增 non_delegated_art_sources_stay_out_of_game_chat_art_gate 钉住契约。 Co-Authored-By: Claude Fable 5 --- .../agent/runtime_driver/main_loop_tests.rs | 47 +++++++++++++++++++ .../src/agent/runtime_tools/file_ops.rs | 8 ++++ 2 files changed, 55 insertions(+) diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/main_loop_tests.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/main_loop_tests.rs index 5175b1eca..443c49466 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/main_loop_tests.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/main_loop_tests.rs @@ -685,6 +685,53 @@ async fn legacy_game_chat_dynamic_art_retry_runs_are_read_only_and_fail_closed() assert!(!root.join("assets/forged-retry-must-not-write.txt").exists()); } +#[test] +fn non_delegated_art_sources_stay_out_of_game_chat_art_gate() { + for source in ["agent-ready-task-scheduler", "agent-background-task"] { + let temporary = tempfile::tempdir().expect("create non-delegated art root"); + let root = temporary.path().join("project"); + init_local_game_project_at(&root, "non-delegated-art", "非委派美术来源") + .expect("init non-delegated art project"); + let run_id = format!("non-delegated-art-{source}"); + start_game_creator_agent_runtime_task_at( + &root, + "art-director", + "非委派来源不得进入 game-chat 美术谱系门", + &run_id, + source, + "校验工具边界", + vec!["非委派来源不受美术门限制".to_string()], + ) + .expect("start non-delegated art runtime"); + for (tool, input) in [ + ( + "command.output_read", + serde_json::json!({"actionId":"history-probe","startLine":1,"maxLines":5}), + ), + ( + "canvas.asset_generate", + serde_json::json!({"prompt":"恢复视觉规范图","outputPath":AGENT_RUNTIME_ART_SPEC_PATH}), + ), + ( + "file.write", + serde_json::json!({"path":"game/notes.txt","content":"scoped by other policies"}), + ), + ] { + assert!( + game_chat_delegated_art_agent_input_mutation_block( + &root, + "art-director", + &run_id, + tool, + &input, + ) + .is_none(), + "{source}/{tool} 不应被 game-chat 美术门拦截" + ); + } + } +} + #[test] fn game_chat_dynamic_art_retry_guard_preserves_dag_art_and_non_art_delegated_retry() { let dag_temporary = tempfile::tempdir().expect("create full DAG art retry root"); diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_tools/file_ops.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_tools/file_ops.rs index e1469b246..90dcdce36 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_tools/file_ops.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_tools/file_ops.rs @@ -146,6 +146,14 @@ fn game_chat_delegated_art_agent_mutation_scope_at( if game_chat_dynamic_art_retry_claims_game_chat_lineage_at(root, &task)? { return Ok(GameChatDelegatedArtAgentMutationScope::RetryLineageBlocked); } + // Live game-chat dynamic art children only ever run with the + // `agent-delegate` source (retry lineage is classified above). + // Scheduler-seeded DAG tasks and plain background runs must not reach + // the strict predicate: it fails closed on a missing run-profile + // binding and would block even their read-only tools. + if task.source != "agent-delegate" { + return Ok(GameChatDelegatedArtAgentMutationScope::Unrestricted); + } } // `assets/**` is a broad write scope. Grant it only after the same // canonical-output authorization used by Canvas replacement has verified