From 37d45d2de8068e58c01570de030043d7f8184185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Fri, 18 Sep 2026 14:03:30 +0800 Subject: [PATCH] =?UTF-8?q?=E9=99=90=E5=88=B6=20DirectProject=20inline=20?= =?UTF-8?q?=E9=99=84=E4=BB=B6=E6=95=B0=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为 canonical 附件增加独立的八个条目上限。 补充超限附件拒绝测试,避免绕过素材引用数量门禁。 --- .../direct_codex_user_item/validation.rs | 40 ++++++++++++++++++- 1 file changed, 39 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 8de0f9440..12bab6797 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 @@ -4,6 +4,7 @@ use super::model::{ }; use crate::agent::{ read_manifest_for_project, sanitize_attachment_local_path, GameCreationAppManifest, + MAX_DIRECT_CODEX_ATTACHMENTS, }; use std::path::Path; @@ -27,6 +28,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; for part in &message.content { match part { DirectCodexUserContentPart::InputText { .. } => {} @@ -39,6 +41,12 @@ pub(crate) fn validate_direct_codex_user_item( validate_runtime_region_reference(&manifest, reference)?; } DirectCodexUserContentPart::AgcAttachmentReference(reference) => { + attachment_count = attachment_count.saturating_add(1); + if attachment_count > MAX_DIRECT_CODEX_ATTACHMENTS { + return Err(format!( + "一次最多携带 {MAX_DIRECT_CODEX_ATTACHMENTS} 个附件" + )); + } if reference.name.trim().is_empty() { return Err("附件缺少文件名".to_string()); } @@ -107,8 +115,9 @@ fn validate_runtime_region_reference( #[cfg(test)] mod tests { - use super::content_has_meaningful_input; + use super::{content_has_meaningful_input, validate_direct_codex_user_item}; use crate::agent::direct_codex_user_item::model::DirectCodexUserContentPart; + use serde_json::json; fn input_text(text: &str) -> DirectCodexUserContentPart { DirectCodexUserContentPart::InputText { @@ -148,4 +157,33 @@ mod tests { }, ])); } + + #[test] + fn inline_attachment_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..=crate::agent::MAX_DIRECT_CODEX_ATTACHMENTS) + .map(|index| { + json!({ + "type": "agc_attachment_reference", + "name": format!("attachment-{index}.txt"), + "mediaType": "text/plain", + "size": 1, + "localPath": "", + "status": "failed" + }) + }) + .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 inline attachments must be rejected"); + assert!(error.contains("最多携带"), "{error}"); + } }