From dc1cac1c15dffd642e967b6c4c1932e703d0607b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Sat, 19 Sep 2026 23:35:41 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=99=20Skill=20=E5=BC=95=E7=94=A8=E8=A1=A5?= =?UTF-8?q?=E4=B8=8A=E6=AF=8F=E6=9D=A1=E6=B6=88=E6=81=AF=E7=9A=84=E6=95=B0?= =?UTF-8?q?=E9=87=8F=E4=B8=8A=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_user_item/validation.rs 新增 MAX_DIRECT_CODEX_SKILL_REFERENCES=32 与 skill_count 计数,超出即拒绝 - 附件与素材引用已有上限,Skill 引用是唯一没有上限的非文本 part:每条都会触发一次 root//SKILL.md 文件探测并往 turn/start.input 里加一项,畸形超长 userItem 可以放大文件探测与模型输入 - 补上 inline_skill_reference_count_is_bounded_independently;cargo test direct_codex_user_item::validation:7 passed --- .../direct_codex_user_item/validation.rs | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_user_item/validation.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_user_item/validation.rs index 5f352dcc6..45ea19f54 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_user_item/validation.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_user_item/validation.rs @@ -10,6 +10,9 @@ use crate::agent::{ use std::path::Path; pub(crate) const MAX_DIRECT_CODEX_REFERENCES: usize = 32; +// Skill 引用也是非文本 part,但没有走 reference_count:它每一条都会触发一次 +// `root//SKILL.md` 文件探测并往 turn input 里加一项,所以单独设上限。 +pub(crate) const MAX_DIRECT_CODEX_SKILL_REFERENCES: usize = 32; pub(crate) fn validate_direct_codex_user_item( root: &Path, @@ -30,6 +33,7 @@ pub(crate) fn validate_direct_codex_user_item( let manifest = read_manifest_for_project(root)?; let mut reference_count = 0usize; let mut attachment_count = 0usize; + let mut skill_count = 0usize; for part in &message.content { match part { DirectCodexUserContentPart::InputText { .. } => {} @@ -38,6 +42,12 @@ pub(crate) fn validate_direct_codex_user_item( validate_resource_id_and_manifest(&manifest, resource_id)?; } DirectCodexUserContentPart::AgcSkillReference { name } => { + skill_count = skill_count.saturating_add(1); + if skill_count > MAX_DIRECT_CODEX_SKILL_REFERENCES { + return Err(format!( + "一次最多引用 {MAX_DIRECT_CODEX_SKILL_REFERENCES} 个 Skill" + )); + } let name = name.trim(); if name.is_empty() || name.chars().count() > 120 @@ -150,7 +160,10 @@ fn validate_runtime_region_reference( #[cfg(test)] mod tests { - use super::{content_has_meaningful_input, validate_direct_codex_user_item}; + use super::{ + content_has_meaningful_input, validate_direct_codex_user_item, + MAX_DIRECT_CODEX_SKILL_REFERENCES, + }; use crate::agent::direct_codex_user_item::model::DirectCodexUserContentPart; use serde_json::json; @@ -246,6 +259,26 @@ mod tests { assert!(error.contains("缺少项目路径"), "{error}"); } + #[test] + fn inline_skill_reference_count_is_bounded_independently() { + let root = tempfile::tempdir().expect("temp project"); + crate::init_local_game_project_at(root.path(), "validation-test", "校验测试") + .expect("init project"); + let content = (0..=MAX_DIRECT_CODEX_SKILL_REFERENCES) + .map(|index| json!({ "type": "agc_skill_reference", "name": format!("skill-{index}") })) + .collect::>(); + let item = serde_json::from_value(json!({ + "type": "message", + "role": "user", + "content": content, + "id": "turn-1:user" + })) + .expect("deserialize user item"); + let error = validate_direct_codex_user_item(root.path(), &item) + .expect_err("too many skill references must be rejected"); + assert!(error.contains("最多引用"), "{error}"); + } + #[test] fn attachment_name_and_media_type_are_bounded_and_well_formed() { let root = tempfile::tempdir().expect("temp project");