From e8d9367df7c99488dfc1e5e7d04527c3775fff04 Mon Sep 17 00:00:00 2001 From: kdletters Date: Fri, 21 Aug 2026 17:38:58 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8DAGC=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=E9=94=81=E6=96=87=E4=BB=B6=E7=BC=BA=E5=A4=B1=20(#174)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 问题 合并后的 `master` 连续暴露了三个既有的 AGC 原生构建 blocker: 1. `npm ci --prefix apps/ai-game-creator-shell` 因 lockfile 缺少 `@emnapi/core` / `@emnapi/runtime` 嵌套节点而失败。 2. 修复安装后,Linux Tauri build 又因通用配置无条件引用未生成的 Windows Codex 侧车而失败。 3. Native shell 全量测试继续暴露三项内置 Skill 指纹从初次提交起即与最终文件字节不一致,以及 Linux 会把 Windows 路径语法误判为普通相对路径。 这些问题都在画布优化合并前的 AGC 集成提交中已经存在;画布优化未修改 AGC manifest、lockfile、Tauri 配置或 Skill Pack。 ## 修复 - 使用子包 manifest 重新生成独立 package-lock 元数据 - 恢复 Tailwind oxide WASM bundled `@emnapi` 节点并保留 npm 元数据规范化 - 把 Codex Windows x64 侧车映射从通用配置迁到 `tauri.windows.conf.json` - 配置门禁同时约束通用配置无 Windows resource、Windows 配置保留完整白名单 - 重算三项内置 Skill 内容指纹并提升审核包版本 - Skill 引用路径按平台无关规则拒绝反斜杠、盘符、UNC、绝对路径和父目录段 - 补充 MCP 回归,并同步技术方案、迁移计划和长期排障记录 ## 验证 - npm 10.9.7 / npm 9.2.0:`npm ci --prefix apps/ai-game-creator-shell` 通过 - Linux:`npm run ai-game-creator-shell:build -- --no-bundle` 通过 - Skill 指纹 5/5 复算匹配;Skill / MCP 定向测试通过 - `npm run check:native-shells` 完整通过:AGC Rust `2159 passed / 0 failed / 15 ignored`,最终输出 `[check:native-shells] OK` - `npm run check:encoding`、`npm run check:rustfmt`、本次目标文件 Prettier、`git diff --check` 通过 - AGC、desktop、server-rs Cargo.lock 与两份 package-lock 均无漂移 Reviewed-on: https://git.genarrative.world/git/GenarrativeAI/Genarrative/pulls/174 Co-authored-by: kdletters Co-committed-by: kdletters --- apps/ai-game-creator-shell/package-lock.json | 53 ++++++------- .../scripts/check-config.mjs | 34 ++++++++ .../resources/agc-skills/manifest.json | 79 +++++++++++++++---- .../src-tauri/src/agent/direct_tools_mcp.rs | 6 ++ .../src-tauri/src/agent/skill_pack.rs | 8 +- .../src-tauri/tauri.conf.json | 10 --- .../src-tauri/tauri.windows.conf.json | 15 ++++ ...计划】AGC直连Codex Runtime迁移-2026-08-15.md | 4 +- docs/project-memory/shared-memory/pitfalls.md | 21 +++++ ...案】AI游戏创作智能体App实施计划-2026-06-24.md | 4 +- 10 files changed, 172 insertions(+), 62 deletions(-) create mode 100644 apps/ai-game-creator-shell/src-tauri/tauri.windows.conf.json diff --git a/apps/ai-game-creator-shell/package-lock.json b/apps/ai-game-creator-shell/package-lock.json index c9f49f1f0..f694b7dd0 100644 --- a/apps/ai-game-creator-shell/package-lock.json +++ b/apps/ai-game-creator-shell/package-lock.json @@ -67,7 +67,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -336,17 +335,6 @@ } } }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz", - "integrity": "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", @@ -2066,6 +2054,27 @@ "node": ">=14.0.0" } }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { "version": "1.2.2", "dev": true, @@ -2517,7 +2526,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -2527,7 +2535,6 @@ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -2611,7 +2618,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.42", "caniuse-lite": "^1.0.30001803", @@ -3133,6 +3139,7 @@ "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz", "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==", "license": "MIT", + "peer": true, "funding": { "type": "GitHub Sponsors ❤", "url": "https://github.com/sponsors/dmonad" @@ -3144,7 +3151,6 @@ "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", "devOptional": true, "license": "MIT", - "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -3201,6 +3207,7 @@ "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.117.tgz", "integrity": "sha512-DeXj9X5xDCjgKLU/7RR+/HQEVzuuEUiwldwOGsHK/sfAfELGWEyTcf0x+uOvCvK3O2zPmZePXWL85vtia6GyZw==", "license": "MIT", + "peer": true, "dependencies": { "isomorphic.js": "^0.2.4" }, @@ -3223,7 +3230,6 @@ "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", "devOptional": true, "license": "MPL-2.0", - "peer": true, "dependencies": { "detect-libc": "^2.0.3" }, @@ -4435,7 +4441,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -4486,7 +4491,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -4575,7 +4579,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -4902,21 +4905,12 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD", - "optional": true - }, "node_modules/typescript": { "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5084,7 +5078,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", diff --git a/apps/ai-game-creator-shell/scripts/check-config.mjs b/apps/ai-game-creator-shell/scripts/check-config.mjs index 04469432d..1f414509d 100644 --- a/apps/ai-game-creator-shell/scripts/check-config.mjs +++ b/apps/ai-game-creator-shell/scripts/check-config.mjs @@ -27,6 +27,12 @@ const tauriConfig = JSON.parse( 'utf8', ), ); +const windowsTauriConfig = JSON.parse( + fs.readFileSync( + new URL('../src-tauri/tauri.windows.conf.json', import.meta.url), + 'utf8', + ), +); const gameChatReleaseTauriConfig = JSON.parse( fs.readFileSync( new URL('../src-tauri/tauri.game-chat-release.conf.json', import.meta.url), @@ -894,6 +900,7 @@ assertNoOpenAiApiKeys([ new URL('../vite.config.ts', import.meta.url), new URL('../src-tauri/Cargo.toml', import.meta.url), new URL('../src-tauri/tauri.conf.json', import.meta.url), + new URL('../src-tauri/tauri.windows.conf.json', import.meta.url), new URL( '../../../packages/shared/src/contracts/gameCreationApp.ts', import.meta.url, @@ -926,6 +933,7 @@ assertNoEnvironmentConfigFallbacks([ new URL('../vite.config.ts', import.meta.url), new URL('../src-tauri/Cargo.toml', import.meta.url), new URL('../src-tauri/tauri.conf.json', import.meta.url), + new URL('../src-tauri/tauri.windows.conf.json', import.meta.url), ]); assertNoNativeBrowserConfirm([new URL('../src/', import.meta.url)]); @@ -1124,6 +1132,32 @@ if (tauriConfig.identifier !== 'world.genarrative.ai-game-creator') { throw new Error('AI game creator shell identifier drifted'); } +const expectedBundledCodexResources = { + 'resources/codex/win-x64/bin/codex.exe': 'codex/win-x64/bin/codex.exe', + 'resources/codex/win-x64/bin/codex-code-mode-host.exe': + 'codex/win-x64/bin/codex-code-mode-host.exe', + 'resources/codex/win-x64/codex-path/rg.exe': + 'codex/win-x64/codex-path/rg.exe', + 'resources/codex/win-x64/codex-resources/codex-command-runner.exe': + 'codex/win-x64/codex-resources/codex-command-runner.exe', + 'resources/codex/win-x64/codex-resources/codex-windows-sandbox-setup.exe': + 'codex/win-x64/codex-resources/codex-windows-sandbox-setup.exe', + 'resources/codex/win-x64/codex-package.json': + 'codex/win-x64/codex-package.json', + 'resources/codex/win-x64/NOTICE.md': 'codex/win-x64/NOTICE.md', + 'resources/codex/win-x64/manifest.json': 'codex/win-x64/manifest.json', +}; +if (tauriConfig.bundle?.resources !== undefined) { + throw new Error( + 'AI game creator shell base Tauri config must not require Windows-only Codex resources', + ); +} +assert.deepEqual( + windowsTauriConfig.bundle?.resources, + expectedBundledCodexResources, + 'AI game creator shell Windows Tauri config must bundle the complete pinned Codex resource set', +); + if (tauriConfig.app?.withGlobalTauri !== true) { throw new Error( 'AI game creator shell must expose window.__TAURI__ for local commands', 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/apps/ai-game-creator-shell/src-tauri/tauri.conf.json b/apps/ai-game-creator-shell/src-tauri/tauri.conf.json index 958af7534..8b3778615 100644 --- a/apps/ai-game-creator-shell/src-tauri/tauri.conf.json +++ b/apps/ai-game-creator-shell/src-tauri/tauri.conf.json @@ -30,16 +30,6 @@ "bundle": { "active": true, "targets": "all", - "resources": { - "resources/codex/win-x64/bin/codex.exe": "codex/win-x64/bin/codex.exe", - "resources/codex/win-x64/bin/codex-code-mode-host.exe": "codex/win-x64/bin/codex-code-mode-host.exe", - "resources/codex/win-x64/codex-path/rg.exe": "codex/win-x64/codex-path/rg.exe", - "resources/codex/win-x64/codex-resources/codex-command-runner.exe": "codex/win-x64/codex-resources/codex-command-runner.exe", - "resources/codex/win-x64/codex-resources/codex-windows-sandbox-setup.exe": "codex/win-x64/codex-resources/codex-windows-sandbox-setup.exe", - "resources/codex/win-x64/codex-package.json": "codex/win-x64/codex-package.json", - "resources/codex/win-x64/NOTICE.md": "codex/win-x64/NOTICE.md", - "resources/codex/win-x64/manifest.json": "codex/win-x64/manifest.json" - }, "linux": { "deb": { "depends": ["bubblewrap"] diff --git a/apps/ai-game-creator-shell/src-tauri/tauri.windows.conf.json b/apps/ai-game-creator-shell/src-tauri/tauri.windows.conf.json new file mode 100644 index 000000000..bcd8ca28b --- /dev/null +++ b/apps/ai-game-creator-shell/src-tauri/tauri.windows.conf.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.tauri.app/config/2", + "bundle": { + "resources": { + "resources/codex/win-x64/bin/codex.exe": "codex/win-x64/bin/codex.exe", + "resources/codex/win-x64/bin/codex-code-mode-host.exe": "codex/win-x64/bin/codex-code-mode-host.exe", + "resources/codex/win-x64/codex-path/rg.exe": "codex/win-x64/codex-path/rg.exe", + "resources/codex/win-x64/codex-resources/codex-command-runner.exe": "codex/win-x64/codex-resources/codex-command-runner.exe", + "resources/codex/win-x64/codex-resources/codex-windows-sandbox-setup.exe": "codex/win-x64/codex-resources/codex-windows-sandbox-setup.exe", + "resources/codex/win-x64/codex-package.json": "codex/win-x64/codex-package.json", + "resources/codex/win-x64/NOTICE.md": "codex/win-x64/NOTICE.md", + "resources/codex/win-x64/manifest.json": "codex/win-x64/manifest.json" + } + } +} 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 236445382..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 @@ -150,7 +150,7 @@ - 真实创建项目时,Codex 0.147.0 能启动 app-server,但首轮文件修改明确失败为缺少 `codex-code-mode-host.exe`;项目只有初始化占位页,未生成游戏。 - 根因是旧 staging 只复制 `bin/codex.exe`,没有保留同版本 npm 原生包依赖的 code-mode host、`rg`、command runner、sandbox setup 和 `codex-package.json` 相对布局。 -- Windows x64 内置资源现按固定白名单 stage 完整原生组件闭包;清单升级为 `genarrative-codex-sidecar.v2`,逐文件记录并校验 SHA-256,Tauri resources 保持 `bin/`、`codex-path/`、`codex-resources/` 布局。API Key、登录状态和用户配置仍不进入安装包。 +- Windows x64 内置资源现按固定白名单 stage 完整原生组件闭包;清单升级为 `genarrative-codex-sidecar.v2`,逐文件记录并校验 SHA-256,Tauri resources 保持 `bin/`、`codex-path/`、`codex-resources/` 布局。资源映射只属于 `tauri.windows.conf.json`,通用配置不得让其他平台在 Tauri build script 阶段校验 Windows 专属文件。API Key、登录状态和用户配置仍不进入安装包。 - 同次真实验收还发现 direct 项目页的可访问名称残留“项目总控”;视觉文案虽已是“陶泥儿”,读屏仍会暴露旧产品模型。direct 分支现统一使用“陶泥儿项目对话 / 陶泥儿消息 / 陶泥儿实时回复 / 陶泥儿对话内容”,旧名称只保留给显式 legacy 诊断入口。 - 修复后同一客户端已真实写入 `game/index.html`、`game/style.css`、`game/game.js`。客户端必须继续把这次真实文件变化确定性登记为游戏代码和项目版本;该步骤不启动 Supervisor、harness、平台美术或二次 LLM。 - direct 产品入口的显式“播放”只做用户授权、本地路径/权限边界和预览服务器启动,不再先执行旧 `game.static_smoke` 的 Canvas-only 形态合同;DOM、Canvas、WebGL 及外链 `game.js` 均可进入客户端运行视图。显式 legacy `/smoke` 与旧诊断入口仍保留原严格检查,不能冒充真实浏览器试玩。 @@ -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 4e1d27acb..358f0ca00 100644 --- a/docs/project-memory/shared-memory/pitfalls.md +++ b/docs/project-memory/shared-memory/pitfalls.md @@ -4216,6 +4216,27 @@ - 处理:CONNECT 一开始就为 client socket 注册 `error / close`,解析完成后为 upstream socket注册同样的双向销毁处理;DNS 返回、写 200 和开始 pipe 前都检查 client 是否已销毁。任一端 error、close 或 timeout 都幂等 destroy 两端,不把普通客户端 reset 写成错误日志。不要用进程级 `uncaughtException` 吞掉问题,也不要只增加 npm/Cargo 重试掩盖 gateway 崩溃。 - 验证:在独立 canary 和正式 gateway 上分别并发制造至少 500 次“CONNECT 后立即断开”,随后确认容器仍运行、restart count 不增加、日志无 EPIPE;再通过同一 proxy 对 npm registry 与 crates index 建立完整 TLS 隧道。切换前仍须确认 Gitea 无活跃 run 且 Runner 内层无 job 容器。 +## 独立 AGC lockfile 不能丢失可选 WASM 包的 bundled 依赖节点(2026-08-21) + +- 现象:`npm ci --prefix apps/ai-game-creator-shell` 在安装前失败,报告 lockfile 缺少 `@emnapi/core` / `@emnapi/runtime`;错误版本可能是 registry 当前满足 `^1.11.1` 的最新版,而不是原 lock 中曾记录的版本。 +- 原因:重写或解决 `apps/ai-game-creator-shell/package-lock.json` 冲突时,保留了 `@tailwindcss/oxide-wasm32-wasi` 对 bundled `@emnapi` 包的声明,却删掉了对应嵌套 package 节点。npm 会重新解析当前 registry 版本并判定 manifest 与 lock 不同步;这不是单一 npm 版本问题,也不表示应用应直接依赖两个 `@emnapi` 包。 +- 处理:只在最新目标分支执行 `npm install --package-lock-only --ignore-scripts --prefix apps/ai-game-creator-shell`,保留 npm 对 bundled 节点及 `peer` / `optional` 标记的完整规范化结果;确认子包 `package.json` 没有变化,不要手工只补报错中的两个版本。 +- 验证:至少用 Jenkins 对应 npm major 和当前开发 npm 分别执行干净的 `npm ci --prefix apps/ai-game-creator-shell`,再运行 AGC typecheck、编码检查和 `git diff --check`;根目录 `npm ci` 不能替代独立子包 lock 验证。 + +## Windows 专属 Tauri resource 不能写进通用配置(2026-08-21) + +- 现象:Linux CI 已完成 AGC `npm ci`,却在 Tauri custom build command 中报 `resources/codex/win-x64/...exe doesn't exist`;Windows 侧车的 Rust staging 受 `cfg(windows)` 保护,因此非 Windows 构建不会生成这些文件。 +- 原因:Tauri 会在所有平台校验通用 `tauri.conf.json` 的 bundle resource 源路径;把 Windows x64 资源映射写进通用配置,等于要求 Linux / macOS 也预先拥有不属于其安装闭包的 Windows 可执行文件。 +- 处理:通用配置只保留跨平台 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 9e7f9e1bb..8982c6ec8 100644 --- a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md +++ b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md @@ -183,7 +183,7 @@ Supervisor 认领该回执后,由父 run 自己为每个原 delivery 逐一创 - 模式合同:客户端 AppData 配置新增全局 `agentMode`,只接受 `codex_cli / provider`。缺省和新安装默认使用 `codex_cli`,原有 HTTP LLM Provider 路径完整保留并可显式切回 `provider`;切换只影响下一次节点请求,不新增 Runner、任务图、会话库、配置库或业务事实源。 - 调度边界:正式 DAG、manifest、Agent task/session/run 身份、队列、锁、委派、all-join、完成门、Provider lifecycle、持久 retry/handoff 与 `needs-reconciliation` 继续由现有 AGC Runtime 掌控。每个被调度节点在 `codex_cli` 模式下直接启动一次非交互 `codex exec` 充当该节点的推理 Agent;Codex 返回当前 Runtime 广告函数的结构化调用,Runtime 仍是唯一 ToolHost,不允许 CLI 自己写项目、执行命令、调用 MCP 或形成第二套 revision / verification 真相。 -- 安装包侧车:Windows x64 release 固定随 Tauri resource 打包 `@openai/codex@0.147.0` 的原生 `codex.exe`;Rust build script 从 AGC 子包锁定依赖 stage 到 resource,并写入版本与 SHA-256 清单。运行时只在文件摘要和 `codex-cli` 版本同时匹配清单时优先选内置侧车;缺失、损坏或版本漂移时跳过它,按既有 npm 安装、PATH 顺序回退。安装包同时携带 Apache-2.0 第三方声明;API Key、`auth.json`、Cookie、Token、用户 `CODEX_HOME`、用户配置和项目数据绝不打包。 +- 安装包侧车:Windows x64 release 固定随 Tauri resource 打包 `@openai/codex@0.147.0` 的原生 `codex.exe`;Rust build script 从 AGC 子包锁定依赖 stage 到 resource,并写入版本与 SHA-256 清单。Windows 侧车映射只写入 `tauri.windows.conf.json`,通用 `tauri.conf.json` 不得让 Linux / macOS 构建依赖未生成的 Windows 二进制。运行时只在文件摘要和 `codex-cli` 版本同时匹配清单时优先选内置侧车;缺失、损坏或版本漂移时跳过它,按既有 npm 安装、PATH 顺序回退。安装包同时携带 Apache-2.0 第三方声明;API Key、`auth.json`、Cookie、Token、用户 `CODEX_HOME`、用户配置和项目数据绝不打包。 - CLI 安全边界:CLI 固定使用 argv 启动,禁止 shell 拼接;工作目录使用本次请求专用的空临时目录,不把游戏项目绝对路径写入 prompt、stdout、stderr 或持久记录。调用固定使用 ephemeral、忽略用户配置和 exec rules、read-only sandbox、never approval,并关闭 Codex shell tool;只继承 CLI 运行和认证所需的最小环境,显式移除宿主 `CODEX_API_KEY`。用户级 Codex 登录态继续由本机 Codex 自己读取,API Key、auth 文件、Cookie、Token、`CODEX_HOME` 私有内容不得复制到项目配置、Runtime sidecar、Agent DB、conversation 或日志;stdout / stderr 无换行时也受硬上限约束,stderr 诊断只记录固定分类、字节数和 SHA-256。 - 协议边界:Runtime 把既有 `LlmRunRequest` 的消息和当前函数目录编码为有界 prompt,并从同一函数 JSON Schema 生成 Codex structured-output schema。CLI 输出转换为现有 `LlmRunResponse / LlmToolCall` 后,继续经过 native tool / MCP 参数校验、动作上限、权限、pending、receipt、验证与格式修复链;最终回复仍走现有脱敏和唯一提交路径,不新增平行响应协议。 - 取消与恢复:Codex 子进程绑定当前 Provider request lifecycle,取消、暂停、Runner draining 或 GUI owner 丢失时终止并回收当前进程;started 后没有可信终态仍沿现有 Provider reconciliation 处理。`agentMode`、CLI 可执行身份和影响输出的 Codex 参数进入 `providerConfigFingerprint`,模式切换不得消费另一模式遗留的 retry/handoff。 @@ -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 行为。