From 00e0fbacde8854e6bbe4e1d72b1ab766996b2184 Mon Sep 17 00:00:00 2001 From: kdletters Date: Fri, 21 Aug 2026 16:46:32 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8DAGC=20Skill=E5=AE=8C=E6=95=B4?= =?UTF-8?q?=E6=80=A7=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更新三项内置 Skill 内容指纹并提升审核包版本 收紧跨平台资源路径校验并补充 MCP 回归 同步技术方案、迁移计划和长期排障记忆 --- .../resources/agc-skills/manifest.json | 79 +++++++++++++++---- .../src-tauri/src/agent/direct_tools_mcp.rs | 6 ++ .../src-tauri/src/agent/skill_pack.rs | 8 +- ...计划】AGC直连Codex Runtime迁移-2026-08-15.md | 2 +- docs/project-memory/shared-memory/pitfalls.md | 7 ++ ...案】AI游戏创作智能体App实施计划-2026-06-24.md | 2 +- 6 files changed, 84 insertions(+), 20 deletions(-) diff --git a/apps/ai-game-creator-shell/src-tauri/resources/agc-skills/manifest.json b/apps/ai-game-creator-shell/src-tauri/resources/agc-skills/manifest.json index ed35c0b29..a098ffbdf 100644 --- a/apps/ai-game-creator-shell/src-tauri/resources/agc-skills/manifest.json +++ b/apps/ai-game-creator-shell/src-tauri/resources/agc-skills/manifest.json @@ -1,46 +1,93 @@ { "schemaVersion": "agc-skill-pack.v1", - "version": "2026-08-20.1", + "version": "2026-08-21.1", "skills": [ { "name": "agc-project-structure", "purpose": "约束当前项目根、游戏代码、美术素材与客户端状态的职责边界", - "triggers": ["创建或修改游戏文件", "判断文件应放置的位置", "读取项目状态证据"], + "triggers": [ + "创建或修改游戏文件", + "判断文件应放置的位置", + "读取项目状态证据" + ], "requiredTools": ["agc_tools.agc_read_skill_resource"], - "files": ["SKILL.md", "agents/openai.yaml", "references/structure-contract.md"], - "sha256": "69b46a2a7180a227dba587dc5eb4ce5b13fb70ab6de55420ee08133d07083225" + "files": [ + "SKILL.md", + "agents/openai.yaml", + "references/structure-contract.md" + ], + "sha256": "348e5e9fc7b2c628223caeb7fe9c0f15477cf97967b90777d3e0462589e13688" }, { "name": "taonier-art-assets", "purpose": "通过受控平台工具生成、恢复、登记并使用真实陶泥儿游戏美术", - "triggers": ["用户要求新美术或重做美术", "新游戏需要平台素材", "已有陶泥儿素材需要接入玩法"], - "requiredTools": ["agc_tools.agc_read_skill_resource", "agc_tools.taonier_prepare_game_art"], - "files": ["SKILL.md", "agents/openai.yaml", "references/platform-art-contract.md"], + "triggers": [ + "用户要求新美术或重做美术", + "新游戏需要平台素材", + "已有陶泥儿素材需要接入玩法" + ], + "requiredTools": [ + "agc_tools.agc_read_skill_resource", + "agc_tools.taonier_prepare_game_art" + ], + "files": [ + "SKILL.md", + "agents/openai.yaml", + "references/platform-art-contract.md" + ], "sha256": "325bbca875aac3d3504ea61519944f3e4f695d3a389e29de5011b5095c74eaa9" }, { "name": "agc-web-game-development", "purpose": "在当前项目实现或修改可玩的 HTML、CSS 与 JavaScript 游戏", - "triggers": ["创建玩法", "修复游戏逻辑", "调整布局与交互", "接入已有素材"], + "triggers": [ + "创建玩法", + "修复游戏逻辑", + "调整布局与交互", + "接入已有素材" + ], "requiredTools": ["agc_tools.agc_read_skill_resource"], - "files": ["SKILL.md", "agents/openai.yaml", "references/game-quality-checklist.md"], - "sha256": "f897a38e89bfc576be9e91ed9bf89c25f54f700aa258039168ae2f7607f147df" + "files": [ + "SKILL.md", + "agents/openai.yaml", + "references/game-quality-checklist.md" + ], + "sha256": "4ecff75e30701d78d65522dd3218d2465b3114a3937c82d538bda1103efd58e4" }, { "name": "agc-browser-playtest", "purpose": "调用客户端真实 desktop/mobile 浏览器试玩并依据证据修复", - "triggers": ["游戏代码发生实质修改", "诊断运行或布局问题", "声明游戏可玩前验收"], - "requiredTools": ["agc_tools.agc_read_skill_resource", "agc_tools.agc_browser_playtest"], - "files": ["SKILL.md", "agents/openai.yaml", "references/browser-evidence-contract.md"], + "triggers": [ + "游戏代码发生实质修改", + "诊断运行或布局问题", + "声明游戏可玩前验收" + ], + "requiredTools": [ + "agc_tools.agc_read_skill_resource", + "agc_tools.agc_browser_playtest" + ], + "files": [ + "SKILL.md", + "agents/openai.yaml", + "references/browser-evidence-contract.md" + ], "sha256": "bc818bcd9a97b7e408d4c81de57a3e52e3e61d858a8d5fb84589e6e47a51cc65" }, { "name": "agc-client-projection", "purpose": "保持游戏代码、美术资源、revision 与正式版本的客户端投影一致", - "triggers": ["新增或替换游戏文件", "素材或版本未显示", "推理 revision 与版本关系"], + "triggers": [ + "新增或替换游戏文件", + "素材或版本未显示", + "推理 revision 与版本关系" + ], "requiredTools": ["agc_tools.agc_read_skill_resource"], - "files": ["SKILL.md", "agents/openai.yaml", "references/projection-contract.md"], - "sha256": "b7b4623032a3eafe55a74f8931c15b380392938985bce4126f16451a1b61de2a" + "files": [ + "SKILL.md", + "agents/openai.yaml", + "references/projection-contract.md" + ], + "sha256": "e9da95c2f371620e045a3ab9f4721078c805e108263f479b4e01b1af96d462d3" } ] } diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tools_mcp.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tools_mcp.rs index 9a953c542..a00978ee0 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tools_mcp.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tools_mcp.rs @@ -446,5 +446,11 @@ mod tests { "relativePath": "../../auth.json" })); assert_eq!(denied["isError"], true); + + let denied_windows_absolute = call_agc_read_skill_resource(&json!({ + "skillName": "agc-project-structure", + "relativePath": r"C:\temp\SKILL.md" + })); + assert_eq!(denied_windows_absolute["isError"], true); } } diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/skill_pack.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/skill_pack.rs index d92414f90..405281980 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/skill_pack.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/skill_pack.rs @@ -108,6 +108,8 @@ struct AgcSkillManifestEntry { fn is_safe_skill_relative_path(value: &str) -> bool { let path = Path::new(value); !value.is_empty() + && !value.contains('\\') + && !value.contains(':') && !path.is_absolute() && path .components() @@ -223,8 +225,7 @@ pub(crate) fn read_agc_skill_resource( relative_path: &str, ) -> Result { let manifest = validated_skill_pack_manifest()?; - let relative_path = relative_path.replace('\\', "/"); - if !is_safe_skill_relative_path(&relative_path) + if !is_safe_skill_relative_path(relative_path) || !relative_path.starts_with("references/") || Path::new(&relative_path) .extension() @@ -320,6 +321,9 @@ mod tests { assert!(!is_safe_skill_relative_path("../SKILL.md")); assert!(!is_safe_skill_relative_path("/tmp/SKILL.md")); assert!(!is_safe_skill_relative_path(r"C:\\temp\\SKILL.md")); + assert!(!is_safe_skill_relative_path("C:/temp/SKILL.md")); + assert!(!is_safe_skill_relative_path(r"\\server\share\SKILL.md")); + assert!(!is_safe_skill_relative_path(r"references\contract.md")); } #[test] diff --git a/docs/project-memory/plans/【实施计划】AGC直连Codex Runtime迁移-2026-08-15.md b/docs/project-memory/plans/【实施计划】AGC直连Codex Runtime迁移-2026-08-15.md index f156d29d9..721ca46db 100644 --- a/docs/project-memory/plans/【实施计划】AGC直连Codex Runtime迁移-2026-08-15.md +++ b/docs/project-memory/plans/【实施计划】AGC直连Codex Runtime迁移-2026-08-15.md @@ -316,7 +316,7 @@ ### 14.2 审核索引与五类 Skill -客户端内置 `agc-skill-pack.v1` 清单。每项只公开名称、用途、触发条件、所需工具、版本和内容 SHA-256;启动时逐文件复核清单和编译进客户端的内容,任何缺失、额外文件、路径越界或指纹不匹配都失败关闭。审核包只包含: +客户端内置 `agc-skill-pack.v1` 清单。每项只公开名称、用途、触发条件、所需工具、版本和内容 SHA-256;审核文件变化时必须在同次变更重算对应指纹。启动时逐文件复核清单和编译进客户端的内容,任何缺失、额外文件、路径越界或指纹不匹配都失败关闭;路径边界显式拒绝反斜杠、Windows 盘符、UNC、绝对路径和 `..`,不能因测试运行在 Linux 就把 Windows 绝对路径当作普通相对文件名。审核包只包含: 1. `agc-project-structure`:项目根、`game/`、`assets/`、`.agent/` 的职责和禁止创建平行项目的约束。 2. `taonier-art-assets`:陶泥儿标准美术包、平台来源、警告语义和真实素材使用;`grid-2x2` 与四切片只是推荐路径,不是所有游戏的完成门。 diff --git a/docs/project-memory/shared-memory/pitfalls.md b/docs/project-memory/shared-memory/pitfalls.md index 84e3837cc..358f0ca00 100644 --- a/docs/project-memory/shared-memory/pitfalls.md +++ b/docs/project-memory/shared-memory/pitfalls.md @@ -4230,6 +4230,13 @@ - 处理:通用配置只保留跨平台 bundle 项;Windows 原生侧车的完整白名单放入 Tauri 自动合并的 `tauri.windows.conf.json`。不要提交二进制占位文件,也不要让非 Windows build script 下载或伪造 Windows 资源。 - 验证:配置门禁断言通用配置没有 Windows resource、Windows 平台配置保留完整固定白名单;Linux 运行原生壳门禁必须越过 Tauri resource 校验,Windows release 仍由 build script 对 npm 原生包、SHA-256 清单和目标布局失败关闭。 +## AGC Skill 指纹与相对路径校验必须跨平台一致(2026-08-21) + +- 现象:内置 Skill 文件集合没有缺失,原生测试却统一报内容指纹不匹配;另一个测试在 Linux 上把 `C:\\temp\\SKILL.md` 判为安全相对路径,受控资源工具可能继续处理 Windows 盘符或反斜杠遍历形式。 +- 原因:审核文件定稿后未按最终字节重新生成 manifest SHA-256;同时 `std::path::Path` 只按当前宿主语义解析路径,Linux 不会把 Windows 盘符和反斜杠视为绝对路径或分隔符。 +- 处理:Skill 文件变化与 manifest 指纹更新必须同次提交,并提升审核包版本;资源引用只接受使用 `/` 的普通相对段,显式拒绝反斜杠、冒号盘符、UNC、绝对路径和父目录段,再查询审核清单。不要先把反斜杠替换成 `/` 后再做安全检查。 +- 验证:逐项按排序后的 `relativePath + NUL + file bytes + NUL` 重算并核对 manifest;Rust 单测同时覆盖 POSIX 绝对路径、`..`、`C:\\...`、`C:/...`、UNC 和反斜杠相对路径,受控 MCP 工具也必须把 Windows 绝对路径投影为 `isError=true`。 + ## Gitea CI 预构建镜像不能只靠 tag 判断内容 - 现象:宿主已重建带日期修订 tag 的 `genarrative/gitea-project-ci` 镜像,但 `genarrative-ci` job 仍跑旧内容,或直接报 image not found;另一种危险操作是只改 runner label,没把对应镜像装入 rootless runner 的内层 Docker。 diff --git a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md index 6c28e7277..8982c6ec8 100644 --- a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md +++ b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md @@ -1161,7 +1161,7 @@ game-project/ ## 2026-08-20 Direct Codex 审核 Skill Pack 与受控工具内核 - 普通项目对话只由一个 project-bound Codex app-server thread 执行。客户端系统提示词只放最小工程合同、当前游戏源码有界快照、项目 prompts 和审核 Skill 索引;不再批量读取项目 `.codex/.agents/.hermes` Skill 正文,也不恢复 Supervisor、专业 Agent 或 harness。 -- `agc-skill-pack.v1` 只包含项目结构、陶泥儿美术、Web 游戏实现、真实浏览器试玩、客户端资源投影五项 Skill。清单记录用途、触发条件、所需工具、版本和内容 SHA-256;客户端把审核文件安装到隔离目录后通过 app-server `skills/extraRoots/set + skills/list` 注册并复核,完整正文由 Codex 原生 Skill 机制按意图加载,一层引用只能经 `agc_read_skill_resource` 读取清单内 Markdown。 +- `agc-skill-pack.v1` 只包含项目结构、陶泥儿美术、Web 游戏实现、真实浏览器试玩、客户端资源投影五项 Skill。清单记录用途、触发条件、所需工具、版本和内容 SHA-256;任何审核文件变化都必须同步重算对应清单指纹。客户端把审核文件安装到隔离目录后通过 app-server `skills/extraRoots/set + skills/list` 注册并复核,完整正文由 Codex 原生 Skill 机制按意图加载,一层引用只能经 `agc_read_skill_resource` 读取清单内 Markdown。引用路径按平台无关规则拒绝反斜杠、盘符、UNC、绝对路径和 `..`,不能依赖当前宿主的 `std::path` 语义判断其它平台路径。 - DirectProject 只连接客户端内置的 `agc_tools` STDIO MCP,工具固定为审核引用读取、标准陶泥儿美术准备和 desktop/mobile 浏览器试玩。MCP 进程只做协议;真实浏览器和付费 External v1 调用通过随机 loopback 地址回到客户端主进程,因此不复制 GUI 登录态、开发者 Key 或项目路径到模型上下文。三项工具固定自动批准,通用 shell、任意网络、多 Agent、插件和外部 MCP 继续关闭。 - 陶泥儿生成继续复用既有私有 Key、持久幂等账本、operation 恢复、来源/下载/PNG 解码和 manifest 登记。完整可信图集缺切片可以继续,固定四切片只是推荐路径;凭据失效、来源不明或结果未知时失败关闭,不能自动换 Key 或重新扣费。 - 自定义 LLM API Key 路由只在 DirectHome/DirectProject 经 loopback `/responses` 流式代理转发。代理不注入 Key,只要求请求自带 Bearer,并剥离开发网关错误携带的 `X-Codex-*` ChatGPT 账户额度头,防止隔离 app-server 把 API Provider 误判为余额 0;旧 ToolHost 保持原 Provider 行为。