diff --git a/apps/ai-game-creator-shell/package.json b/apps/ai-game-creator-shell/package.json index 45dce94ac..781be78d2 100644 --- a/apps/ai-game-creator-shell/package.json +++ b/apps/ai-game-creator-shell/package.json @@ -1,7 +1,7 @@ { "name": "@genarrative/ai-game-creator-shell", "private": true, - "version": "0.1.5", + "version": "0.1.6", "type": "module", "scripts": { "dev": "node scripts/start-tauri-dev.mjs", diff --git a/apps/ai-game-creator-shell/scripts/check-config.mjs b/apps/ai-game-creator-shell/scripts/check-config.mjs index 92d2aa756..3200ce18c 100644 --- a/apps/ai-game-creator-shell/scripts/check-config.mjs +++ b/apps/ai-game-creator-shell/scripts/check-config.mjs @@ -1544,11 +1544,11 @@ if ( } if ( - tauriConfig.version !== '0.1.5' || - packageConfig.version !== '0.1.5' || - cargoPackageVersion !== '0.1.5' + tauriConfig.version !== '0.1.6' || + packageConfig.version !== '0.1.6' || + cargoPackageVersion !== '0.1.6' ) { - throw new Error('AI game creator standard release must remain version 0.1.5'); + throw new Error('AI game creator standard release must remain version 0.1.6'); } const devServerSource = fs.readFileSync( diff --git a/apps/ai-game-creator-shell/src-tauri/Cargo.lock b/apps/ai-game-creator-shell/src-tauri/Cargo.lock index e6e7ab45b..a5ddc0425 100644 --- a/apps/ai-game-creator-shell/src-tauri/Cargo.lock +++ b/apps/ai-game-creator-shell/src-tauri/Cargo.lock @@ -1695,7 +1695,7 @@ dependencies = [ [[package]] name = "genarrative-ai-game-creator-shell" -version = "0.1.5" +version = "0.1.6" dependencies = [ "agent-runtime-core", "axum", diff --git a/apps/ai-game-creator-shell/src-tauri/Cargo.toml b/apps/ai-game-creator-shell/src-tauri/Cargo.toml index bfcc5ba3a..6ec5d0790 100644 --- a/apps/ai-game-creator-shell/src-tauri/Cargo.toml +++ b/apps/ai-game-creator-shell/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "genarrative-ai-game-creator-shell" -version = "0.1.5" +version = "0.1.6" edition = "2021" publish = false diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime.rs index 46ea6faba..739d10711 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime.rs @@ -13,7 +13,7 @@ const MAX_DIRECT_HOME_ATTACHMENT_MEDIA_TYPE_CHARS: usize = 96; const MIN_DIRECT_CLIENT_TURN_ID_CHARS: usize = 6; const MAX_DIRECT_CLIENT_TURN_ID_CHARS: usize = 160; const DIRECT_TAONIER_IDENTITY_GUIDANCE: &str = "对外身份合同:你是“陶泥儿”,是 Genarrative 的游戏创作助手。用户询问你是谁、你的名称或能力时,以陶泥儿的身份回答;不要把 Codex、ChatGPT、OpenAI、模型、通用 AI 助手或内部执行智能体当作自己的名称或对外身份。Codex app-server 仅是客户端内部执行技术;只有用户明确询问底层实现时才可如实说明,同时仍以陶泥儿自称。"; -const DIRECT_AGC_ENGINEERING_GUIDANCE: &str = "AGC 工程合同:当前 Codex cwd 是项目真实 `game/` 源码目录,只允许把项目源码写入该目录;`../assets/` 只能按审核 Skill 或 `agc_tools` 返回的相对路径使用,不要用原生文件/命令工具遍历父目录;`.agent/` 和项目根由客户端维护,不能请求扩权或直接改写。DirectProject 提供 Codex 原生文件、搜索、命令、图片查看、Skill,以及经审核的 `agc_tools` MCP;浏览器试玩、平台美术、资源登记和搜索等带 AGC 账本的动作使用 `agc_tools`。按用户意图自行选择并执行,不要等待 Supervisor、harness 或宿主规划器。不要读取或输出凭据、Token、Cookie、auth.json、.env 或宿主私密路径。项目锁、付费提交、幂等账本、下载校验和客户端投影仍由客户端确定性掌管。游戏文件真实变化后由客户端登记资源和版本,Codex 不直接保存或伪造项目版本。"; +const DIRECT_AGC_ENGINEERING_GUIDANCE: &str = "AGC 工程合同:当前 Codex cwd 是项目真实 `game/` 源码目录,只允许把项目源码写入该目录;原生文件工具、原生 patch 和命令参数中的文件路径必须相对于当前 cwd:合法写法是 `index.html`、`style.css`、`game.js`,禁止写 `game/index.html`、`../game/index.html`、项目根绝对路径或任何其它父目录路径;`game/...` 只用于 AGC 回执、manifest 和客户端投影,不用于 cwd 内的原生 patch。`../assets/` 只能按审核 Skill 或 `agc_tools` 返回的相对路径使用,不要用原生文件/命令工具遍历父目录;`.agent/` 和项目根由客户端维护,不能请求扩权或直接改写。DirectProject 提供 Codex 原生文件、搜索、命令、图片查看、Skill,以及经审核的 `agc_tools` MCP;浏览器试玩、平台美术、资源登记和搜索等带 AGC 账本的动作使用 `agc_tools`。按用户意图自行选择并执行,不要等待 Supervisor、harness 或宿主规划器。不要读取或输出凭据、Token、Cookie、auth.json、.env 或宿主私密路径。项目锁、付费提交、幂等账本、下载校验和客户端投影仍由客户端确定性掌管。游戏文件真实变化后由客户端登记资源和版本,Codex 不直接保存或伪造项目版本。"; const DIRECT_CODEX_ART_SPEC_ASSET_PATH: &str = "assets/art-spec.png"; const DIRECT_CODEX_BACKGROUND_ASSET_PATH: &str = "assets/direct-game-background.png"; const DIRECT_CODEX_SPRITESHEET_ASSET_PATH: &str = "assets/art-spritesheet.png"; @@ -4406,6 +4406,8 @@ mod tests { assert!(!prompt.contains("你是 Codex")); assert!(prompt.contains("不要等待 Supervisor")); assert!(prompt.contains("提示词与技能")); + assert!(prompt.contains("合法写法是 `index.html`、`style.css`、`game.js`")); + assert!(prompt.contains("禁止写 `game/index.html`、`../game/index.html`")); } #[test] diff --git a/apps/ai-game-creator-shell/src-tauri/src/project/resource_editor.rs b/apps/ai-game-creator-shell/src-tauri/src/project/resource_editor.rs index 2e2a4cdcd..a8a05fc70 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/project/resource_editor.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/project/resource_editor.rs @@ -4732,6 +4732,21 @@ fn resource_edit_pending_is_visible_to_current_principal( } } +/// A task-scoped Developer Key is deliberately independent from the GUI's platform session. +/// When credentials are already resolved for the current operation, an absent session means +/// Developer mode and must not be filled from the process-global account snapshot. +fn select_resource_edit_frozen_platform_session( + remote_credentials: Option<&(String, String, Option)>, + is_remote_media: bool, + current_session: Option, +) -> Option { + match remote_credentials { + Some((_, _, frozen_session)) => frozen_session.clone(), + None if is_remote_media => current_session, + None => None, + } +} + pub(crate) async fn request_resource_edit_service_identity_confirmation_at( input: RequestResourceEditServiceIdentityConfirmationInput, ) -> Result { @@ -5201,16 +5216,11 @@ pub(crate) async fn derive_local_project_resource_at( } else { None }; - let frozen_platform_session = remote_credentials - .as_ref() - .and_then(|(_, _, session)| session.clone()) - .or_else(|| { - input - .edit_kind - .is_remote_media() - .then(current_platform_session) - .flatten() - }); + let frozen_platform_session = select_resource_edit_frozen_platform_session( + remote_credentials.as_ref(), + input.edit_kind.is_remote_media(), + current_platform_session(), + ); if input.edit_kind.is_remote_media() && ledger.platform_owner_user_id.is_some() && frozen_platform_session.is_none() @@ -5337,6 +5347,41 @@ mod tests { const PROJECT_ID: &str = "resource-editor-test-project"; + #[test] + fn developer_credentials_do_not_inherit_gui_platform_session() { + let gui_session = PlatformSessionSnapshot { + user_id: "gui-owner".to_string(), + access_token: "gui-token".to_string(), + api_base_url: "https://dev.genarrative.world".to_string(), + generation: 7, + }; + let developer_credentials = ( + "https://dev.genarrative.world".to_string(), + "tnr_sk_direct_fixture".to_string(), + None, + ); + assert_eq!( + select_resource_edit_frozen_platform_session( + Some(&developer_credentials), + true, + Some(gui_session.clone()), + ), + None, + "Developer Key mode must not be paired with the GUI token snapshot" + ); + + let platform_credentials = ( + gui_session.api_base_url.clone(), + gui_session.access_token.clone(), + Some(gui_session.clone()), + ); + assert_eq!( + select_resource_edit_frozen_platform_session(Some(&platform_credentials), true, None,), + Some(gui_session), + "platform mode must retain the session captured with its credentials" + ); + } + async fn with_test_external_editor_credentials( api_base_url: &str, api_key: &str, 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 ebf2a8031..35a3a1cd0 100644 --- a/apps/ai-game-creator-shell/src-tauri/tauri.conf.json +++ b/apps/ai-game-creator-shell/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Genarrative AI Game Creator", - "version": "0.1.5", + "version": "0.1.6", "identifier": "world.genarrative.ai-game-creator", "build": { "beforeDevCommand": "npm --prefix ../.. run agc:serve", diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 419113081..134f60bc4 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -14734,3 +14734,9 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在 - 客户端:画布点击 `ui-prototype` 先幂等桥接到 `UI` JSON,并立即刷新 manifest;关联查找按 canonical resource identity 且优先已完成 workflow 资源。全部页面完成后,工作台自动打开首个页面的 UI 编辑器 `visual-binding` 最终阶段。 - 完成门:`finalize` 必须为每个页面提供 `game/` 下真实 UTF-8 应用文件并安装当前 UI State revision 标记;缺少结构、组件、页面或标记时拒绝完成。详细输入、阶段与恢复契约见 [`docs/【技术方案】UI工作流资源桥接与Runtime执行-2026-08-24.md`](../../【技术方案】UI工作流资源桥接与Runtime执行-2026-08-24.md)。 - 验证:前端 bridge 6/6、资源实时集成 19/19、AppSurface 410/410、AGC typecheck、Rust workflow 定向测试覆盖 provider 前的 reference 阶段与真实调用失败关闭、Rust bridge 1/1、编码、格式和 diff 门禁通过;认证登录与真实 Provider 生成的桌面端 E2E 尚未具备可用会话,保持未验证。 + +## 2026-08-25 AGC DirectProject 直连路径与 Developer Key 身份隔离 + +- DirectProject 的 Codex cwd 固定为真实 `game/` 目录时,原生文件工具和 patch 必须使用 cwd 相对路径(`index.html`、`style.css`、`game.js`);`game/...` 仅用于 AGC manifest、回执和客户端投影,不能作为 cwd 内原生 patch 路径,以避免 `writing outside of the project`。 +- 直连 Runtime 已取得 Developer Key 时,资源编辑的 `remote_credentials` 是该操作的完整身份边界;其中冻结平台快照为空表示 Developer 模式,禁止再从进程全局 GUI 登录态补回账号快照。平台账号模式仍只使用同一组凭据捕获的快照。 +- 回归覆盖 Direct 系统提示路径合同和 Developer Key / GUI 快照隔离;未触碰用户项目 `.agent` 锁、账本或凭据。 diff --git a/package-lock.json b/package-lock.json index ea4a1c8f3..1c68f410e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -93,7 +93,7 @@ }, "apps/ai-game-creator-shell": { "name": "@genarrative/ai-game-creator-shell", - "version": "0.1.5", + "version": "0.1.6", "dependencies": { "@cubone/react-file-manager": "^1.35.0", "@genarrative/image-canvas-core": "0.1.0", diff --git a/scripts/check-npm-workspaces.mjs b/scripts/check-npm-workspaces.mjs index dab4563b4..245d19bea 100644 --- a/scripts/check-npm-workspaces.mjs +++ b/scripts/check-npm-workspaces.mjs @@ -174,7 +174,7 @@ export function collectNpmWorkspaceErrors(rootDir) { ); } const expectedWorkspaceVersion = - workspacePath === 'apps/ai-game-creator-shell' ? '0.1.5' : '0.1.0'; + workspacePath === 'apps/ai-game-creator-shell' ? '0.1.6' : '0.1.0'; if (manifest.version !== expectedWorkspaceVersion) { errors.push( `${manifestPath}: workspace version must be ${expectedWorkspaceVersion}`, diff --git a/scripts/check-npm-workspaces.test.mjs b/scripts/check-npm-workspaces.test.mjs index e2c3bf95f..50c69843a 100644 --- a/scripts/check-npm-workspaces.test.mjs +++ b/scripts/check-npm-workspaces.test.mjs @@ -79,7 +79,7 @@ function createValidFixture() { name: workspaceNames[workspacePath], private: true, version: - workspacePath === 'apps/ai-game-creator-shell' ? '0.1.5' : '0.1.0', + workspacePath === 'apps/ai-game-creator-shell' ? '0.1.6' : '0.1.0', dependencies: localDependencies[workspacePath], }; writeJson(rootDir, `${workspacePath}/package.json`, manifest);