From 755bf808587a7cb663c458160e1f102a4a676ff8 Mon Sep 17 00:00:00 2001
From: kdletters <61648117+kdletters@users.noreply.github.com>
Date: Tue, 11 Aug 2026 13:25:02 +0800
Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=20AI=20=E6=B8=B8=E6=88=8F?=
=?UTF-8?q?=E5=88=9B=E4=BD=9C=E6=89=93=E5=BC=80=E7=8E=B0=E6=9C=89=20Godot?=
=?UTF-8?q?=20=E9=A1=B9=E7=9B=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
新增 Godot 项目选择、校验、导入和项目上下文切换入口。
只在所选项目根保留 .agent 元数据,并让总控使用标准运行档。
修复发布版原生 API 访问和 React runtime 重复加载问题。
收紧 Provider 工具 Schema 子集并增加回归门禁。
补充 Rust、前端测试及 PRD、技术方案和共享决策记录。
---
apps/ai-game-creator-shell/package-lock.json | 10 ++
apps/ai-game-creator-shell/package.json | 1 +
.../src-tauri/Cargo.lock | 167 +++++++++++++++++-
.../src-tauri/Cargo.toml | 1 +
.../src-tauri/capabilities/main.json | 4 +
.../src-tauri/src/agent_native_tools.rs | 3 +-
.../src-tauri/src/commands.rs | 23 +++
.../src-tauri/src/main.rs | 3 +
.../src-tauri/src/project/manifest.rs | 78 +++++++-
.../src/project/manifest/import_tests.rs | 67 +++++++
.../src-tauri/src/tests/project.rs | 1 +
apps/ai-game-creator-shell/src/App.tsx | 64 +++++--
.../src/app/AuthenticatedClient.tsx | 64 ++++++-
apps/ai-game-creator-shell/src/app/types.ts | 4 +
.../features/app-shell/ProjectCreation.tsx | 6 +
.../features/app-shell/WorkspaceLauncher.tsx | 2 +
.../src/features/app-shell/model.ts | 1 +
.../app-shell/useHomeProjectCreation.ts | 68 +++++++
.../src/services/clientApi.ts | 20 +--
.../src/services/clientAuth.ts | 20 +--
.../src/services/clientHttp.ts | 58 ++++++
.../src/view/home/index.tsx | 11 ++
.../appSurface/project-development.suite.ts | 91 +++++++++-
.../tests/clientHttp.test.ts | 51 ++++++
.../tests/clientRuntimeErrorBoundary.test.ts | 37 ++++
apps/ai-game-creator-shell/vite.config.ts | 3 +
...AI游戏创作】项目开发工作台PRD-2026-07-20.md | 9 +-
.../shared-memory/decision-log.md | 19 ++
...案】AI游戏创作智能体App实施计划-2026-06-24.md | 8 +-
package-lock.json | 33 ++++
package.json | 1 +
scripts/check-native-shells.mjs | 83 ++++++++-
32 files changed, 932 insertions(+), 79 deletions(-)
create mode 100644 apps/ai-game-creator-shell/src-tauri/src/project/manifest/import_tests.rs
create mode 100644 apps/ai-game-creator-shell/src/services/clientHttp.ts
create mode 100644 apps/ai-game-creator-shell/tests/clientHttp.test.ts
create mode 100644 apps/ai-game-creator-shell/tests/clientRuntimeErrorBoundary.test.ts
diff --git a/apps/ai-game-creator-shell/package-lock.json b/apps/ai-game-creator-shell/package-lock.json
index 048c92537..f0fb8cac3 100644
--- a/apps/ai-game-creator-shell/package-lock.json
+++ b/apps/ai-game-creator-shell/package-lock.json
@@ -11,6 +11,7 @@
"@lexical/react": "^0.47.0",
"@lexical/utils": "^0.47.0",
"@tauri-apps/plugin-clipboard-manager": "2.3.2",
+ "@tauri-apps/plugin-http": "^2.5.9",
"@tauri-apps/plugin-opener": "~2",
"@vitejs/plugin-react": "^5.0.4",
"lexical": "^0.47.0",
@@ -2208,6 +2209,15 @@
"@tauri-apps/api": "^2.8.0"
}
},
+ "node_modules/@tauri-apps/plugin-http": {
+ "version": "2.5.9",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-http/-/plugin-http-2.5.9.tgz",
+ "integrity": "sha512-lCiY0+vs4HvIUSvZrBs8TC3TiCB0MOPRmiUjTq4prW7SlcJE2jdLeT6KBsJrT9Tlplufl7W1pY6SFAO3gCWxDA==",
+ "license": "MIT OR Apache-2.0",
+ "dependencies": {
+ "@tauri-apps/api": "^2.11.0"
+ }
+ },
"node_modules/@tauri-apps/plugin-opener": {
"version": "2.5.4",
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-opener/-/plugin-opener-2.5.4.tgz",
diff --git a/apps/ai-game-creator-shell/package.json b/apps/ai-game-creator-shell/package.json
index e2982bbc3..dfb31f3c2 100644
--- a/apps/ai-game-creator-shell/package.json
+++ b/apps/ai-game-creator-shell/package.json
@@ -37,6 +37,7 @@
"@lexical/react": "^0.47.0",
"@lexical/utils": "^0.47.0",
"@tauri-apps/plugin-clipboard-manager": "2.3.2",
+ "@tauri-apps/plugin-http": "^2.5.9",
"@tauri-apps/plugin-opener": "~2",
"@vitejs/plugin-react": "^5.0.4",
"lexical": "^0.47.0",
diff --git a/apps/ai-game-creator-shell/src-tauri/Cargo.lock b/apps/ai-game-creator-shell/src-tauri/Cargo.lock
index ec50f66c7..89a23f4f5 100644
--- a/apps/ai-game-creator-shell/src-tauri/Cargo.lock
+++ b/apps/ai-game-creator-shell/src-tauri/Cargo.lock
@@ -702,10 +702,39 @@ version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
dependencies = [
+ "percent-encoding",
"time",
"version_check",
]
+[[package]]
+name = "cookie_store"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206"
+dependencies = [
+ "cookie",
+ "document-features",
+ "idna",
+ "log",
+ "publicsuffix",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "time",
+ "url",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
[[package]]
name = "core-foundation"
version = "0.10.1"
@@ -729,7 +758,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97"
dependencies = [
"bitflags 2.13.0",
- "core-foundation",
+ "core-foundation 0.10.1",
"core-graphics-types",
"foreign-types 0.5.0",
"libc",
@@ -742,7 +771,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
dependencies = [
"bitflags 2.13.0",
- "core-foundation",
+ "core-foundation 0.10.1",
"libc",
]
@@ -889,6 +918,12 @@ version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8"
+[[package]]
+name = "data-url"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376"
+
[[package]]
name = "dbus"
version = "0.9.11"
@@ -1018,6 +1053,15 @@ dependencies = [
"syn 2.0.118",
]
+[[package]]
+name = "document-features"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
+dependencies = [
+ "litrs",
+]
+
[[package]]
name = "dom_query"
version = "0.27.0"
@@ -1131,6 +1175,15 @@ version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
+[[package]]
+name = "encoding_rs"
+version = "0.8.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
+dependencies = [
+ "cfg-if",
+]
+
[[package]]
name = "endi"
version = "1.1.1"
@@ -1616,6 +1669,7 @@ dependencies = [
"tauri-build",
"tauri-plugin-clipboard-manager",
"tauri-plugin-dialog",
+ "tauri-plugin-http",
"tauri-plugin-opener",
"tempfile",
"tokio",
@@ -1835,6 +1889,25 @@ dependencies = [
"syn 2.0.118",
]
+[[package]]
+name = "h2"
+version = "0.4.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "http",
+ "indexmap 2.14.0",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
[[package]]
name = "half"
version = "2.7.1"
@@ -1955,6 +2028,7 @@ dependencies = [
"bytes",
"futures-channel",
"futures-core",
+ "h2",
"http",
"http-body",
"httparse",
@@ -1979,6 +2053,7 @@ dependencies = [
"tokio",
"tokio-rustls",
"tower-service",
+ "webpki-roots",
]
[[package]]
@@ -2015,9 +2090,11 @@ dependencies = [
"percent-encoding",
"pin-project-lite",
"socket2",
+ "system-configuration",
"tokio",
"tower-service",
"tracing",
+ "windows-registry",
]
[[package]]
@@ -2488,6 +2565,12 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
+[[package]]
+name = "litrs"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
+
[[package]]
name = "lock_api"
version = "0.4.14"
@@ -3734,6 +3817,22 @@ dependencies = [
"windows 0.62.2",
]
+[[package]]
+name = "psl-types"
+version = "2.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
+
+[[package]]
+name = "publicsuffix"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf"
+dependencies = [
+ "idna",
+ "psl-types",
+]
+
[[package]]
name = "pxfm"
version = "0.1.30"
@@ -3987,8 +4086,12 @@ checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
dependencies = [
"base64 0.22.1",
"bytes",
+ "cookie",
+ "cookie_store",
+ "encoding_rs",
"futures-core",
"futures-util",
+ "h2",
"http",
"http-body",
"http-body-util",
@@ -3998,6 +4101,7 @@ dependencies = [
"hyper-util",
"js-sys",
"log",
+ "mime",
"native-tls",
"percent-encoding",
"pin-project-lite",
@@ -4021,6 +4125,7 @@ dependencies = [
"wasm-bindgen-futures",
"wasm-streams 0.4.2",
"web-sys",
+ "webpki-roots",
]
[[package]]
@@ -4291,7 +4396,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
dependencies = [
"bitflags 2.13.0",
- "core-foundation",
+ "core-foundation 0.10.1",
"core-foundation-sys",
"libc",
"security-framework-sys",
@@ -4847,6 +4952,27 @@ dependencies = [
"syn 2.0.118",
]
+[[package]]
+name = "system-configuration"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b"
+dependencies = [
+ "bitflags 2.13.0",
+ "core-foundation 0.9.4",
+ "system-configuration-sys",
+]
+
+[[package]]
+name = "system-configuration-sys"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
[[package]]
name = "system-deps"
version = "6.2.2"
@@ -4868,7 +4994,7 @@ checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9"
dependencies = [
"bitflags 2.13.0",
"block2",
- "core-foundation",
+ "core-foundation 0.10.1",
"core-graphics",
"crossbeam-channel",
"dbus",
@@ -5103,6 +5229,30 @@ dependencies = [
"url",
]
+[[package]]
+name = "tauri-plugin-http"
+version = "2.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5bd512048e1985b7ec78f96d99083e2ddaf7e0d906b2b63c44ce5bb8b894067"
+dependencies = [
+ "bytes",
+ "cookie_store",
+ "data-url",
+ "http",
+ "regex",
+ "reqwest 0.12.28",
+ "schemars 0.8.22",
+ "serde",
+ "serde_json",
+ "tauri",
+ "tauri-plugin",
+ "tauri-plugin-fs",
+ "thiserror 2.0.18",
+ "tokio",
+ "url",
+ "urlpattern",
+]
+
[[package]]
name = "tauri-plugin-opener"
version = "2.5.4"
@@ -6176,6 +6326,15 @@ dependencies = [
"system-deps",
]
+[[package]]
+name = "webpki-roots"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a"
+dependencies = [
+ "rustls-pki-types",
+]
+
[[package]]
name = "webview2-com"
version = "0.38.2"
diff --git a/apps/ai-game-creator-shell/src-tauri/Cargo.toml b/apps/ai-game-creator-shell/src-tauri/Cargo.toml
index 59f16c386..06aad7379 100644
--- a/apps/ai-game-creator-shell/src-tauri/Cargo.toml
+++ b/apps/ai-game-creator-shell/src-tauri/Cargo.toml
@@ -41,6 +41,7 @@ reqwest = { version = "0.12", default-features = false, features = ["json", "nat
shared-contracts = { path = "../../../server-rs/crates/shared-contracts", default-features = false }
tauri = { version = "2.11.2", features = [] }
tauri-plugin-dialog = "2.7.1"
+tauri-plugin-http = "2.5.9"
tauri-plugin-opener = "2"
tempfile = "3"
tokio = { version = "1", features = ["io-util", "macros", "process", "rt-multi-thread", "signal", "sync", "time"] }
diff --git a/apps/ai-game-creator-shell/src-tauri/capabilities/main.json b/apps/ai-game-creator-shell/src-tauri/capabilities/main.json
index 7f4653d2f..50d312f47 100644
--- a/apps/ai-game-creator-shell/src-tauri/capabilities/main.json
+++ b/apps/ai-game-creator-shell/src-tauri/capabilities/main.json
@@ -9,6 +9,10 @@
"core:image:allow-rgba",
"core:image:allow-size",
"core:resources:allow-close",
+ {
+ "identifier": "http:default",
+ "allow": [{ "url": "https://dev.genarrative.world/api/*" }]
+ },
"opener:default"
]
}
diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent_native_tools.rs b/apps/ai-game-creator-shell/src-tauri/src/agent_native_tools.rs
index b1fab7688..51c9d2e1e 100644
--- a/apps/ai-game-creator-shell/src-tauri/src/agent_native_tools.rs
+++ b/apps/ai-game-creator-shell/src-tauri/src/agent_native_tools.rs
@@ -1262,7 +1262,6 @@ fn runtime_tool_input_schema(tool: &str) -> Value {
"missingAssetSlots": {
"type": "array",
"maxItems": 2,
- "uniqueItems": true,
"items": { "type": "string", "enum": ["art-spec", "core-spritesheet"] }
}
}
@@ -1386,7 +1385,7 @@ mod tests {
let Some(object) = schema.as_object() else {
return;
};
- for keyword in ["oneOf", "anyOf", "allOf", "not"] {
+ for keyword in ["oneOf", "anyOf", "allOf", "not", "uniqueItems"] {
if object.contains_key(keyword) {
issues.push(format!(
"strict schema contains unsupported {keyword} at {path}"
diff --git a/apps/ai-game-creator-shell/src-tauri/src/commands.rs b/apps/ai-game-creator-shell/src-tauri/src/commands.rs
index 7e1fb6bc0..4f3af8430 100644
--- a/apps/ai-game-creator-shell/src-tauri/src/commands.rs
+++ b/apps/ai-game-creator-shell/src-tauri/src/commands.rs
@@ -12,6 +12,18 @@ pub(crate) fn init_local_game_project(
init_local_game_project_at(root, project_id.trim(), name.trim())
}
+#[tauri::command]
+pub(crate) fn import_local_godot_project(
+ project_path: String,
+ project_id: String,
+ name: String,
+) -> Result {
+ let root = Path::new(project_path.trim());
+ enforce_project_permission_policy(root, "project.create")?;
+ let _lock = acquire_project_write_lock(root, "project.create")?;
+ import_local_godot_project_at(root, project_id.trim(), name.trim())
+}
+
#[tauri::command]
pub(crate) fn is_local_project_directory_non_empty(project_path: String) -> Result {
let root = Path::new(project_path.trim());
@@ -58,6 +70,7 @@ pub(crate) fn inspect_local_project_directory(
exists: root.exists(),
is_directory: root.is_dir(),
is_game_creator_project: is_game_creator_project_directory(root),
+ is_godot_project: is_godot_project_directory(root),
project_name: game_creator_project_name(root),
manifest_error: game_creator_project_manifest_error(root),
recent_run_status: recent_run_trace.as_ref().map(|trace| trace.status.clone()),
@@ -65,6 +78,16 @@ pub(crate) fn inspect_local_project_directory(
})
}
+pub(crate) fn is_godot_project_directory(root: &Path) -> bool {
+ if !root.is_dir() {
+ return false;
+ }
+ let project_file = root.join("project.godot");
+ fs::symlink_metadata(project_file)
+ .map(|metadata| metadata.is_file() && !metadata.file_type().is_symlink())
+ .unwrap_or(false)
+}
+
pub(crate) fn is_game_creator_project_directory(root: &Path) -> bool {
if !root.is_dir() {
return false;
diff --git a/apps/ai-game-creator-shell/src-tauri/src/main.rs b/apps/ai-game-creator-shell/src-tauri/src/main.rs
index 81ee7ca50..2428dabf6 100644
--- a/apps/ai-game-creator-shell/src-tauri/src/main.rs
+++ b/apps/ai-game-creator-shell/src-tauri/src/main.rs
@@ -124,6 +124,7 @@ struct LocalProjectDirectoryStatus {
exists: bool,
is_directory: bool,
is_game_creator_project: bool,
+ is_godot_project: bool,
project_name: Option,
manifest_error: Option,
recent_run_status: Option,
@@ -2036,6 +2037,7 @@ fn main() {
let app = tauri::Builder::default()
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_dialog::init())
+ .plugin(tauri_plugin_http::init())
.plugin(tauri_plugin_clipboard_manager::init())
.manage(game_creator_preview_registry())
.setup(move |app| {
@@ -2169,6 +2171,7 @@ fn main() {
})
.invoke_handler(tauri::generate_handler![
init_local_game_project,
+ import_local_godot_project,
is_local_project_directory_non_empty,
inspect_local_project_directory,
pick_local_project_directory,
diff --git a/apps/ai-game-creator-shell/src-tauri/src/project/manifest.rs b/apps/ai-game-creator-shell/src-tauri/src/project/manifest.rs
index 80216aa36..4f1e01b7e 100644
--- a/apps/ai-game-creator-shell/src-tauri/src/project/manifest.rs
+++ b/apps/ai-game-creator-shell/src-tauri/src/project/manifest.rs
@@ -138,7 +138,12 @@ fn try_open_manifest_write_lock_file(path: &Path) -> Result
+