Compare commits

...

59 Commits

Author SHA1 Message Date
kdletters 1a7abfd059 AGC 项目定时快照上传(目标 OSS agc-dev) (#400)
Project CI / AI game creator shell Rust shard 1/4 (push) Waiting to run
Project CI / AI game creator shell Rust shard 2/4 (push) Waiting to run
Project CI / AI game creator shell Rust shard 3/4 (push) Waiting to run
Project CI / AI game creator shell Rust shard 4/4 (push) Waiting to run
Project CI / AI game creator shell Rust smoke (push) Waiting to run
Project CI / AI game creator shell Rust crates (push) Waiting to run
Project CI / Backend tests (push) Waiting to run
Project CI / Native shell tests (push) Waiting to run
Project CI / Frontend tests (push) Waiting to run
Project CI / Repository checks (push) Waiting to run
Project CI / AI game creator shell web tests (push) Waiting to run
## 交付内容

- 客户端新增 `project_snapshot` 模块:项目扫描与排除口径、增量索引、差异对比、上传编排、状态查询。
- 触发:工作区窗口存活期间的周期定时器 + 工作区窗口关闭(`CloseRequested`);应用退出只在有界预算内等待在途同步收尾,不重复发起。
- 服务端新增两条登录态路由 `POST /api/agc/project-snapshots/files` 与 `/manifest`,由 api-server 用服务端凭据写入私有前缀 `agc/project-snapshots/v1/{user}/{project}/`;客户端不持有 OSS 凭据、不直连 OSS。
- `platform-oss` 新增内部对象精确写入与探测、项目快照对象键构造;修复 `head_internal_object` 读 HEAD 响应长度恒为 0(会导致服务端"已存在即跳过"永不生效)。
- `shared-contracts` 新增 `agc_project_snapshots` DTO 与项目 ID、相对路径、摘要校验。
- 真实 OSS 存储层冒烟示例 + 客户端真实链路冒烟用例(`#[ignore]`,env 驱动)。
- 登记 `check-config.mjs` 的 native-only 命令白名单,恢复 `npm run agc` 可启动。

## 验证证据

- 存储层:`cargo run -p platform-oss --example agc_project_snapshot_live_smoke` 对真实 `agc-dev` 完成写入 → 读回(contentLength 32)→ 清单写入 → 探针清理。
- HTTP 层:登录态下 7 项校验(401 无 token、400 路径穿越/长度不一致/非法摘要/空正文/重复路径清单)全部符合预期且不写对象。
- 客户端链路(真实项目 gameagent-033b6cf3…):第一次 `synced uploaded=6 uploadedBytes=36694 remoteSkipped=6`,紧接着第二次 `no-op uploaded=0`。
- GUI 触发:`trigger=periodic ... uploaded=6`;把周期设为 600 秒排除干扰、改一个文件后关窗得到 `trigger=project-close ... revision=2 uploaded=1`,索引摘要由 `28d837cd84f8ab62` 推进到 `bff34e2e336d901f`(同步在进程退出前完成)。
- 门禁:AGC `project_snapshot` 14 passed(+1 ignored live)、api-server `project_snapshots` 3 passed、platform-oss 39 passed、shared-contracts 87 passed、`cargo fmt --check`(两处)、`check:encoding`、`check:doc-index`、`git diff --check`、`check-config.mjs` 全部通过。

## 已知未决(不阻塞本里程碑)

- 远端对象只增不减:没有删除路径,也没有 bucket 生命周期规则;要收口需要先定保留语义(清单引用 GC 还是 OSS 生命周期)。
- 部署环境需确认 `GENARRATIVE_AGC_PROJECT_SNAPSHOT_OSS_*` 或回退的 `ALIYUN_OSS_*` 具备目标 bucket 私有前缀的 `PutObject` 权限;未配置时接口返回 503、客户端失败关闭(不推进索引)。
- 缺少按用户/项目的配额与限流(error_reports 有每小时提交上限的先例,本功能没有)。
- 没有界面入口与状态可见性:`read_local_project_snapshot_state` 已注册但未接 UI,失败只写本机日志。

## 不做

- 不做云端下载/恢复、跨设备合并、版本回滚。
- 不改 `/api/external/v1` 与 External OpenAPI,不新增 SpacetimeDB 表。

---------

Co-authored-by: kdletters <61648117+kdletters@users.noreply.github.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/400
2026-09-17 18:34:51 +08:00
kdletters 1c5ff8f852 回填 AGC 更新渠道首次发布证据
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / AI game creator shell Rust shard 1/4 (push) Has been cancelled
- 记录 Genarrative-Agc-Windows-Build #68 成功发布 dev-win 渠道 0.1.48 与迁移桥
- 记录签名对象与渠道清单逐字一致、安装包尺寸与 SHA-256 与迁移桥登记一致
- 待执行项仅保留客户端真实升级闭环与签名失败拒绝安装
2026-09-17 18:25:26 +08:00
kdletters ce04d8cef4 AGC 发布流水线增加 OSS 演练开关
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 5m35s
Project CI / AI game creator shell Rust shard 2/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (push) Has been cancelled
- Jenkins 增加布尔参数 AGC_RELEASE_DRY_RUN,勾选后只构建并打印上传命令,不写入 OSS
- 参数转换为脚本读取的 AGC_RELEASE_DRY_RUN 环境变量,完成提示区分演练与真发
2026-09-17 18:14:57 +08:00
lhk229 504e26da43 修复策划agent panic死掉问题 (#405)
Project CI / AI game creator shell Rust shard 1/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Reviewed-on: https://git.genarrative.world/git/GenarrativeAI/Genarrative/pulls/405
2026-09-17 18:11:54 +08:00
kdletters fc14190f58 图集切片模式改为必须显式声明并补齐决策要求 (#408)
Project CI / AI game creator shell Rust shard 1/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
## 背景

切图新增基于连通域的切分后,LLM 仍倾向显式传 `sliceMode=grid`:参数只存在于部分 LLM 可见面、带默认值、没有任何决策规则,生成结果也不回显生效模式。

## 变更

- 平台:`/api/editor/icon-spritesheets/generations` 与 `/api/external/v1/editor/icon-spritesheets/generations` 把 `sliceMode` 改为必填并移除默认值;缺失、空白或未知取值在引用解析、定价与任何 provider / OSS 副作用之前返回 `400`,错误统一带 `field` 与决策要求。
- 契约:`grid` 必须同时提供 `gridX`/`gridY`,`connected-components` 不接受网格尺寸;`sliceCount` 只约束连通域切分,请求与响应的公开上限统一为 `256`;OpenAPI 去掉默认值并补必填与失败语义。
- AGC:MCP 工具说明去掉默认值并补决策要求,桥接层新增可测试的切分声明校验;原生工具 `canvas.asset_generate` 暴露 `sliceMode/gridX/gridY/sliceCount` 并要求图集显式声明;生成结果回显 `sliceMode/gridX/gridY` 与 `slicePaths`;严格图集在本地提交前校验平台回显与请求声明一致。
- 标准美术包:显式声明 `connected-components` 加 `sliceCount=4`,并在四张 canonical 切片用途映射前校验数量,禁止截断或错位。
- 前端与画板:画板 Agent 工具装配与画板提交计划显式声明连通域切分;前端类型要求显式 `sliceMode` 并在本地校验声明自洽。
- 文档与 Skill:主规范、OpenAPI、AGC Skill、外部编辑器 Skill、里程碑与实施计划、共享决策记录同步更新。
- 测试环境:测试构建对提权 Windows 主机上系统临时目录的所有者偏差做一次性所有者初始化重试,临时目录之外的越权所有者继续失败关闭。

## 兼容性影响

省略 `sliceMode` 的旧调用方(含已发布但未更新的 AGC 客户端与第三方外部 API 调用方)会在图集生成上收到 `400`;这是本次"不允许默认值"的预期结果,仓库内自有调用方已全部改为显式声明。

## 验证

- 平台:`slice_mode_must_be_declared_*` 与 OpenAPI 契约测试通过;全量 `cargo test -p api-server` 1043 通过 / 11 失败(`wallet_refund_outbox` 临时文件 `拒绝访问`,已在改动前基线复现,属本机环境)。
- AGC:`slice` 30、`spritesheet` 21、`direct_tools_mcp` 23、`agent_native_tools` 16、`canvas_generation_tests` 83、提示词上限与桥接门禁各 1 条、`cargo check --tests` 全部通过。
- 前端:182 条定向测试与 `typecheck` 通过。
- 门禁:`cargo fmt --check`(两个 workspace)、`check:encoding`、`check:doc-index`、`git diff --check` 通过。
- 未验证:真实 Provider 与浏览器试玩、确定性 e2e 车道;整机全量 AGC 单进程运行在本机受提权 shell 的所有者与时序问题影响,不作为门禁信号。

---------

Co-authored-by: kdletters <61648117+kdletters@users.noreply.github.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/408
2026-09-17 18:10:34 +08:00
kdletters 5e4ff54a9d 删除模板工程内置的嵌套 package-lock
Project CI / AI game creator shell Rust shard 1/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
- 模板 fixture 内的 package-lock.json 触发 workspace 守卫的「禁止嵌套 lockfile」规则,导致 master 门禁失败
- 其余五个模板都只提交 project 内容与 package.json,删除该文件与它们保持一致
- 模板 zip 由发布脚本现场打包并重新计算大小与哈希,仓库内没有引用该 lock 的脚本或测试
2026-09-17 18:04:21 +08:00
kdletters 1f5b7b250a 修正首页推荐位用例断言到模板库
- 首页「灵感推荐」已被模板库推荐位替换,用例改为断言模板库推荐卡片、已下载徽标与共 N 个模板计数
- 用例改为断言点击推荐位进入模板库页面,且不触发下载或由模板创建项目
- 保留不请求已退役展示流接口的断言
2026-09-17 18:03:15 +08:00
kdletters a5fd25f10a 客户端更新切换到官方更新插件并按渠道分发
- 客户端接入 tauri-plugin-updater:原生侧注册插件,删除自研更新下载命令、下载进度事件与安装器启动逻辑
- 客户端更新服务与更新提示改走官方插件接口,删除自研清单解析、版本比较与下载实现
- 更新能力只授予客户端主窗口,移除只为自研清单放行的 OSS 白名单与 CSP 连接项
- 新增构建期更新检查开关:开发态默认关闭,agc 启动不请求更新清单、不显示更新入口
- 发布脚本按渠道生成官方更新插件清单与签名,universal macOS 产物同时挂两个平台键,缺签名失败关闭
- 发布脚本按渠道上传安装包、签名与渠道清单,并为 dev-win 生成旧协议 sha256 迁移清单
- 构建期按渠道注入更新端点配置,渠道与目标平台不匹配时发布失败关闭
- Jenkins 流水线新增渠道参数与签名凭据注入,归档补充签名与迁移清单
- 新增发布上传 dry-run 开关,只打印 ossutil 命令且不回显凭据
- 更新技术方案与开发运维文档,登记 macOS 渠道落地待办
2026-09-17 17:56:53 +08:00
kdletters 5db407c3dc 新增 AGC 模板库与由模板创建项目链路 (#396)
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 7m12s
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 7m18s
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 7m25s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 7m22s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m49s
Project CI / AI game creator shell Rust crates (push) Successful in 2m36s
Project CI / Repository checks (push) Failing after 1m11s
Project CI / Frontend tests (push) Failing after 4m37s
Project CI / AI game creator shell web tests (push) Failing after 4m6s
Project CI / Native shell tests (push) Successful in 9m39s
Project CI / Backend tests (push) Successful in 10m12s
## 交付范围

- OSS `agc-dev` bucket 的 `templates/` 前缀作为 AGC 游戏模板库(**去掉 `agc/` 这一层**;`agc/` 继续只放客户端安装包与更新清单)。
- 模板正文改为 zip(zip 根 == AGC 项目根,如 `game/index.html`);清单补齐 `tags`、封面字段与包大小/摘要,`templateVersion` 用于判断是否需要重下。
- 客户端:Rust 模板库模块、模板库全屏页、首页模板推荐位、左侧导航入口。

## 客户端行为

- `fetch_game_template_library`:读取并校验清单(≤4 MiB),缓存到 `<app_data>/templates/index.json`;网络失败回退本机缓存并标注 `source=cache`。
- `download_game_template`:按清单下载 zip(≤512 MiB),校验字节数与 SHA-256 后解压到 `<app_data>/templates/installed/<id>/<version>/`,最后写 `installed.json` 作为安装完成标记。
- `create_automatic_local_game_project_from_template`:先把模板文件铺进新项目目录,再走既有 `init_local_game_project_at` 补 `.agent` 清单与标准目录;失败删除半成品目录。
- 解压只接受普通文件与目录(拒绝绝对路径、`..`、盘符、符号链接),安装目录名由标识符白名单拼出;远端只信「受信任 OSS 主机 + 对象键」,清单里的地址字段不参与请求。
- UI:搜索(空白分隔多关键词「与」)、标签/运行时筛选、仅看已下载、封面卡片与已下载徽标;「使用模板」在版本落后时先重下再建项。

## 文档与脚本

- 主规范 `docs/technical/【技术方案】AGC模板库与模板建项-2026-09-17.md`;里程碑与实施计划见 `docs/project-memory/plans/`;`decision-log.md` 记录库路径、清单 schema、安装缓存目录与 CSP 约定。
- 发布脚本 `scripts/agc-template-library-publish.mjs`(`--dry-run` 校验 → 上传 → 回读校验)。
- 首页「灵感推荐」替换为模板库推荐位,删除本机灵感图目录与 `InspirationGallery.tsx`;`tauri.conf.json` 的 `img-src` 放行受信任 OSS 主机用于加载封面。

## 验证

- `cargo test --bin genarrative-ai-game-creator-shell template_library`:9 passed;`--ignored` 真连检查 `fetches_the_live_template_library_index` 通过。
- `npx vitest run src/features/template-library`:9 passed。
- `npm run typecheck`、`cargo fmt --check`、`npm run check:encoding`、`node scripts/check-doc-index.mjs`、`git diff --check` 均通过。
- OSS 侧匿名复核:`templates/index.json`、各 `cover.png` 与 `template.zip` 均返回 200。

## 未做 / 待确认

- 本轮未做客户端点击级人工验收(下一步单独验证并把结果补进此 PR)。
- 模板封面目前是占位标题卡,正式封面由后续模板包提供。

---------

Co-authored-by: kdletters <61648117+kdletters@users.noreply.github.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/396
2026-09-17 17:48:06 +08:00
lhk229 de9e8e0c7f 修复运行时默认重试次数的测试断言
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Successful in 2m7s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 4m22s
Project CI / AI game creator shell Rust crates (push) Successful in 2m5s
Project CI / Repository checks (push) Successful in 5m27s
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 7m9s
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 6m10s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 6m27s
Project CI / Frontend tests (push) Successful in 6m34s
Project CI / Native shell tests (push) Successful in 7m57s
Project CI / Backend tests (push) Successful in 10m8s
Project CI / AI game creator shell web tests (push) Has been cancelled
将恢复默认配置的重试次数断言同步为10。
补充默认配置变更时的测试核对规则。
2026-09-17 17:03:45 +08:00
lhk229 c958e1955b Merge branch 'master' into opt/design-llm-call
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 6m57s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 6m54s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 7m18s
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 7m37s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m55s
Project CI / AI game creator shell Rust crates (push) Successful in 2m17s
Project CI / Frontend tests (push) Failing after 3m52s
Project CI / Repository checks (push) Failing after 3m52s
Project CI / Native shell tests (push) Successful in 8m39s
Project CI / AI game creator shell web tests (push) Failing after 4m17s
Project CI / Backend tests (push) Successful in 9m45s
2026-09-17 16:26:16 +08:00
lhk229 1b579acf47 提高客户端LLM默认重试次数到10
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
内置默认值 DEFAULT_GAME_CREATOR_LLM_MAX_RETRIES 由 2 改为 10
随App分发的配置模板 game-creator.config.json 的 maxRetries 同步为 10
设置对话框草稿兜底值 maxRetries 同步为 10
2026-09-17 08:22:52 +00:00
k88936 ff84fa7b04 Merge pull request '修复推理档弹层被裁剪问题,增加 CSS 覆盖声明以确保窄布局下弹层完整显示。' (#401) from fix/effort-panel-overlay into master
Project CI / AI game creator shell Rust shard 3/4 (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / AI game creator shell Rust shard 1/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (push) Has been cancelled
Reviewed-on: #401
2026-09-17 16:21:52 +08:00
k88936 1b56c0a586 Merge remote-tracking branch 'origin/master' into fix/effort-panel-overlay
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 5m32s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 6m18s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 4m56s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m5s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 4m14s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m58s
Project CI / Repository checks (pull_request) Successful in 4m14s
Project CI / Frontend tests (pull_request) Successful in 5m14s
Project CI / Native shell tests (pull_request) Successful in 8m38s
Project CI / Backend tests (pull_request) Successful in 9m40s
Project CI / AI game creator shell web tests (pull_request) Successful in 4m55s
# Conflicts:
#	docs/project-memory/shared-memory/pitfalls.md
2026-09-17 15:56:56 +08:00
k88936 efdb364a52 修复推理档弹层被裁剪问题,增加 CSS 覆盖声明以确保窄布局下弹层完整显示。
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
2026-09-17 15:54:07 +08:00
lhk229 ebc5dfe7db 修复策划工具乱序局部修改导致的中断 (#399)
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 6m39s
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 5m11s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m2s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 5m29s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 4m18s
Project CI / AI game creator shell Rust crates (push) Successful in 2m47s
Project CI / Repository checks (push) Successful in 4m9s
Project CI / Frontend tests (push) Successful in 5m20s
Project CI / Native shell tests (push) Successful in 8m16s
Project CI / Backend tests (push) Successful in 9m49s
Project CI / AI game creator shell web tests (push) Successful in 4m4s
按原文位置排序并顺序拼接补丁,避免替换偏移失效。
新增乱序、中文与长度变化的局部修改回归测试。
同步策划 Agent 批量局部修改技术说明。

Reviewed-on: https://git.genarrative.world/git/GenarrativeAI/Genarrative/pulls/399
2026-09-17 14:58:21 +08:00
kdletters cd9faae9b2 修复 agc_tools 媒体资源提示词上限并按 kind 暴露 (#398)
Project CI / AI game creator shell Rust shard 3/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 1/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
## 改动

- 提示词上限收敛为 `resource_edit_prompt_max_chars` 单一权威:`agc_tools` MCP 工具层、客户端受控工具桥与提交校验共用同一数字与拒绝文案,不再出现「schema 写 4000、真实上限按 kind 分」的分叉。
- `agc_create_or_derive_resource` 的 schema 逐 kind 声明 `prompt.maxLength`(background-music / sound-effect / video 与 character-animation),`prompt` 描述写明每个数字;`agc_edit_image` 继续用图片口径 32000。
- 工具桥不再用通用 4000 校验 prompt:图片编辑 4000~32000 的提示词不再被误报成「超出安全边界」。
- 源资源未登记时的错误文案补上可执行两步:先用 `agc_list_registered_assets` 选已有 localAssetId;只在项目里的文件先 `agc_list_project_files` 确认 `assetImportable=true`,再用 `agc_import_account_assets.localPaths` 登记后重试。`agc_remove_background` 复用同一条文案。
- skill 包 `agc-client-projection` 的 SKILL.md 与 projection-contract.md 写明四个上限,并说明超限要在本地收敛而不是原样重发;清单指纹同步到 `2026-08-26.18`(已合并 master 的抠图文案)。
- 前端 `resourceEditModel.ts` 补注释指向 Rust 权威函数,数字未动。
- decision-log、pitfalls 与《AI游戏创作智能体App实施计划》记录本轮口径;未改 External v1 契约 / OpenAPI、SpacetimeDB schema 与客户端 UI 行为。

## 问题

| 编号 | 问题 | 本 PR 状态 |
| --- | --- | --- |
| P1 | 背景音乐提示词真实上限 140,但 schema 只写 4000、skill 未写明,agent 写一句正常长度描述必被硬拒 | 已修复:口径单源 + 逐 kind 暴露 + skill 写明 |
| P2 | 音效 1900 与图片编辑 32000 的上限只在 Rust 内部生效,MCP 与工具桥各自硬编码或误报 | 已修复:三处共用同一函数,图片编辑不再误报安全边界 |
| P3 | 源资源未登记时只报「不属于当前项目已登记资源」,模型会原地重试 | 已修复:文案给出 `agc_list_registered_assets` 与登记工具两步动作 |
| P4 | 这两个工具此前没有端到端用例,同类漂移无法被门禁拦住 | 已补:MCP 工具层 → 真实工具桥 → 假平台的契约用例 + 两条按 kind 上限门禁 |

## 验证

- `agent::direct_tools_mcp::tests` 23 passed / 0 failed:含新增的按 kind 上限门禁、图片快速编辑契约(断言 `POST /api/editor/images/edits` 的路径、Bearer、Idempotency-Key、正文且不得回填 assetKind)、背景音乐契约(断言 `POST /api/editor/audios/background-music/generations` 的 `gptDescriptionPrompt` 与 `makeInstrumental`)、超限零桥请求、未登记源资源提示。
- `agent::direct_tool_bridge::tests` 18 passed / 7 failed;`agent::skill_pack` 4 passed;`npm run agc:skill-pack:check` 与 `skill-pack:test` 通过(version `2026-08-26.18`)。
- AGC `typecheck`(tsc + skill-pack check + check-config)、`resourceEditModel` 6 项前端测试、`npm run check:encoding`、`npm run check:doc-index`、`cargo fmt --check`、`git diff --check` 通过。
- 那 7 条(以及 `project::resource_editor` 45 条)失败与本 PR 无关:全部是 `tempfile::tempdir()` 的 `Windows 安全对象不属于当前用户`,已 stash 改动并用基线复跑确认失败集合一致。
- 未跑整包 AGC Rust 全量分片、`npm run lint` 聚合门禁与 CI;未做任何付费生成。工具 schema 与 skill 文案都在应用内,终端用户需重新构建客户端才能看到修复效果。

---------

Co-authored-by: kdletters <61648117+kdletters@users.noreply.github.com>
Reviewed-on: #398
2026-09-17 14:56:56 +08:00
suzmii d85622069d Merge pull request '修复 AGC 画布交互、JSON 识别与对话历史分页' (#390) from fix/agc-canvas-json into master
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 6m27s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m12s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 6m6s
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 6m48s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 6m23s
Project CI / AI game creator shell Rust crates (push) Successful in 3m16s
Project CI / Frontend tests (push) Successful in 4m23s
Project CI / Repository checks (push) Successful in 5m0s
Project CI / Backend tests (push) Successful in 7m48s
Project CI / AI game creator shell web tests (push) Successful in 3m38s
Project CI / Native shell tests (push) Successful in 10m45s
Reviewed-on: #390
Reviewed-by: 孔令弘 <ink29535@proton.me>
2026-09-17 14:02:12 +08:00
suzmii 036395c6c9 修复历史重读后旧消息追加到末尾的乱序
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 7m9s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m24s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 6m26s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 6m49s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 7m23s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m49s
Project CI / Repository checks (pull_request) Failing after 4m16s
Project CI / Frontend tests (pull_request) Successful in 5m46s
Project CI / Native shell tests (pull_request) Successful in 9m22s
Project CI / Backend tests (pull_request) Successful in 10m6s
Project CI / AI game creator shell web tests (pull_request) Failing after 4m22s
区分历史回读与实时消息来源,保留运行时所有权语义
防止旧页消息在历史刷新后追加到最新回复末尾
增加重新读取历史后再次翻页的顺序和去重回归
同步对话历史规范与实施计划
2026-09-17 13:54:00 +08:00
suzmii d1f53fda4d Merge branch 'master' into fix/agc-canvas-json
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 6m49s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 7m3s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m21s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 7m56s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 6m50s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 3m1s
Project CI / Frontend tests (pull_request) Successful in 4m38s
Project CI / Repository checks (pull_request) Successful in 3m43s
Project CI / Backend tests (pull_request) Successful in 8m30s
Project CI / AI game creator shell web tests (pull_request) Successful in 4m7s
Project CI / Native shell tests (pull_request) Successful in 25m34s
2026-09-17 13:05:33 +08:00
lhk229 9169ae0dad Merge pull request '修复前端测试状态泄漏与异步断言竞态' (#397) from codex/ci-frontend-stability-20260917 into master
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 6m15s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 6m59s
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 7m18s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 7m46s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m0s
Project CI / AI game creator shell Rust crates (push) Successful in 2m21s
Project CI / Frontend tests (push) Successful in 5m22s
Project CI / Repository checks (push) Successful in 4m26s
Project CI / Backend tests (push) Successful in 9m22s
Project CI / AI game creator shell web tests (push) Successful in 3m53s
Project CI / Native shell tests (push) Successful in 9m49s
Reviewed-on: https://git.genarrative.world/git/GenarrativeAI/Genarrative/pulls/397
2026-09-17 12:29:26 +08:00
lhk229 a68a9621a7 修复前端测试状态泄漏与异步断言竞态
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 7m4s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 6m12s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m43s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 7m7s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 4m48s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m7s
Project CI / Repository checks (pull_request) Successful in 4m50s
Project CI / Frontend tests (pull_request) Successful in 5m52s
Project CI / Backend tests (pull_request) Successful in 9m13s
Project CI / Native shell tests (pull_request) Successful in 8m3s
Project CI / AI game creator shell web tests (pull_request) Successful in 5m0s
隔离创作主页换签网络请求并使用独立响应

恢复快捷键选择状态、持久化路由和UI编辑页宿主mock

修正虚拟树尺寸与选择时序并纳入换签Hook测试

记录全量与随机顺序测试验证方式
2026-09-17 11:56:17 +08:00
suzmii 9d374a57b1 修正运行不可用提示的过期测试契约
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 5m28s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 5m54s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 5m32s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m0s
Project CI / Backend tests (pull_request) Failing after 11s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m17s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 5m52s
Project CI / Repository checks (pull_request) Failing after 11s
Project CI / Frontend tests (pull_request) Successful in 5m46s
Project CI / AI game creator shell web tests (pull_request) Successful in 4m15s
Project CI / Native shell tests (pull_request) Successful in 9m21s
恢复运行提示与资源选择解耦的业务逻辑
更新界面源码契约测试,保留资源选中交互回归
补充运行提示及无障碍关联的验收口径
2026-09-17 11:51:14 +08:00
lhk229 d9431b51da 隔离创作主页资源预览测试缓存
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 7m6s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 7m16s
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 7m38s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 7m56s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m6s
Project CI / AI game creator shell Rust crates (push) Successful in 2m36s
Project CI / Frontend tests (push) Failing after 4m59s
Project CI / Repository checks (push) Successful in 4m53s
Project CI / Native shell tests (push) Successful in 9m52s
Project CI / Backend tests (push) Successful in 10m33s
Project CI / AI game creator shell web tests (push) Successful in 4m13s
每个用例前后清理签名资源地址缓存,避免共享调度状态影响活动图片断言
2026-09-17 10:05:22 +08:00
suzmii 7f9dce5f79 修复画布运行不可用提示契约
恢复资源选中状态下运行提示的正确显示条件

使源码契约测试与实际行为保持一致
2026-09-17 09:48:19 +08:00
lhk229 9beb16f203 稳定CI测试并发与工作流 (#393)
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 7m12s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 7m25s
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 7m46s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 8m8s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m51s
Project CI / AI game creator shell Rust crates (push) Successful in 2m8s
Project CI / Frontend tests (push) Failing after 4m23s
Project CI / Repository checks (push) Successful in 4m31s
Project CI / Backend tests (push) Successful in 9m14s
Project CI / Native shell tests (push) Successful in 9m5s
Project CI / AI game creator shell web tests (push) Successful in 5m19s
为Project CI增加同分支并发取消,避免旧运行覆盖新结果

将Vitest默认线程数固定为1,降低异步UI测试竞态

Reviewed-on: #393
Co-authored-by: Linghong <ink29535@proton.me>
Co-committed-by: Linghong <ink29535@proton.me>
2026-09-17 09:18:39 +08:00
lhk229 9b6ed8a4cf 整理抠图测试导入并明确对外说明边界
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 7m13s
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 5m52s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m49s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 4m50s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 6m48s
Project CI / AI game creator shell Rust crates (push) Successful in 2m41s
Project CI / Frontend tests (push) Successful in 5m30s
Project CI / Repository checks (push) Successful in 4m22s
Project CI / Native shell tests (push) Successful in 9m3s
Project CI / Backend tests (push) Successful in 9m19s
Project CI / AI game creator shell web tests (push) Successful in 5m12s
将主站与客户端抠图测试模块的导入统一放到模块顶部
明确 provider 选择与兜底策略仅保留在内部技术方案中
2026-09-17 00:54:28 +08:00
lhk229 16deb0ad61 补齐抠图参数透传与验收
修复flat自动识别门禁并严格校验模式和颜色

保持旧请求幂等指纹并区分客户端抠图意图

同步工具说明和Skill契约并补充定向测试

记录真实BgFilter验证及本地数据库阻塞的待验收项
2026-09-17 00:54:28 +08:00
lhk229 13f56e644d 补充抠图模式调用说明
说明complex语义分割与flat纯色背景抠图

明确纯色背景优先使用flat并移除内部实现术语
2026-09-17 00:54:28 +08:00
lhk229 e08b031761 调整抠图模式参数说明 2026-09-17 00:54:28 +08:00
lhk229 70ad794b28 扩展AGC抠图客户端参数
支持complex与flat模式及auto背景色

保留旧客户端参数调用兼容性
2026-09-17 00:54:28 +08:00
lhk229 ca761e093c 接通抠图模式到BgFilter队列
透传backgroundMode和screenColor到内部worker

保留auto及省略颜色的下游识别语义
2026-09-17 00:54:28 +08:00
lhk229 a17367112f 增加主站抠图参数归一化
缺省模式兼容为complex并校验颜色格式

将auto颜色原样保留给BgFilter
2026-09-17 00:54:28 +08:00
lhk229 d7a0c3cffd 扩展主站抠图请求契约
增加backgroundMode和screenColor可选字段

同步External v1 OpenAPI抠图模式说明
2026-09-17 00:54:28 +08:00
lhk229 fa64819b6e 冻结BgFilter抠图服务契约
记录flat与complex模式及auto背景色行为

补充服务器认证头和当前运行版本
2026-09-17 00:54:28 +08:00
lhk229 f748d36d21 补充AGC抠图模式透传方案
新增主站与BgFilter模式及背景色契约

补充AGC客户端同步任务与兼容性规则

记录联调验收和发布回滚边界
2026-09-17 00:54:28 +08:00
suzmii ecaf1e8cec Merge branch 'master' into fix/agc-canvas-json
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 4m50s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 6m22s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 5m19s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m9s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m45s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 5m38s
Project CI / Frontend tests (pull_request) Failing after 3m50s
Project CI / Repository checks (pull_request) Failing after 4m18s
Project CI / Native shell tests (pull_request) Successful in 8m2s
Project CI / AI game creator shell web tests (pull_request) Failing after 3m31s
Project CI / Backend tests (pull_request) Successful in 8m50s
2026-09-16 22:58:25 +08:00
lhk229 79b153e3fa 修复Direct活动回合重试类型错误
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 6m53s
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 7m5s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 7m26s
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 7m51s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m49s
Project CI / AI game creator shell Rust crates (push) Successful in 2m23s
Project CI / Frontend tests (push) Failing after 4m42s
Project CI / Repository checks (push) Successful in 4m44s
Project CI / Backend tests (push) Successful in 9m27s
Project CI / Native shell tests (push) Successful in 9m17s
Project CI / AI game creator shell web tests (push) Failing after 5m2s
重试等待 Promise 显式声明为 void,修复 shell typecheck 与原生构建失败。

补充卸载后取消读取重试的回归测试。
2026-09-16 21:53:19 +08:00
lhk229 362edcc49d 修复前端测试偶发超时与异步清理
Direct 活动回合读取失败后的重试定时器随组件卸载清理,避免测试环境销毁后访问 window。

后台素材查询大量缩略图测试合并可见性触发,并补充独立超时预算。

创作首页活动图与资源卡预览调度重载用例分别补充独立超时预算。

清单快照测试补齐 Direct 活动回合 IPC mock,避免未预期命令进入重试。
2026-09-16 21:53:19 +08:00
kdletters 1e434e0cb5 文档记录 CI 宿主 CPU 上限:Jenkins 16 核 / Gitea Actions runner 12 核
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 6m57s
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 7m10s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 7m35s
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 7m56s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m46s
Project CI / AI game creator shell Rust crates (push) Successful in 1m47s
Project CI / Repository checks (push) Successful in 3m34s
Project CI / Frontend tests (push) Successful in 4m25s
Project CI / Native shell tests (push) Successful in 7m56s
Project CI / AI game creator shell web tests (push) Successful in 4m32s
Project CI / Backend tests (push) Successful in 10m1s
- 在【开发运维】本地开发验证与生产运维新增「CI 宿主 CPU 上限」小节,写明 jenkins.service CPUQuota=1600% 与 gitea-stack runner cpus=12.0 的生效位置、核验命令和回滚方式
- 在 shared-memory/decision-log.md 补记该资源上限决策的背景、边界与验证证据
2026-09-16 21:17:30 +08:00
lhk229 cec971c438 修复局部修改工具的缺陷和偶发锁问题 (#386)
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 5m49s
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 4m50s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m53s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 5m16s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 4m1s
Project CI / AI game creator shell Rust crates (push) Successful in 2m14s
Project CI / Frontend tests (push) Failing after 4m6s
Project CI / Repository checks (push) Successful in 4m33s
Project CI / Native shell tests (push) Successful in 6m33s
Project CI / Backend tests (push) Successful in 8m26s
Project CI / AI game creator shell web tests (push) Successful in 3m43s
Reviewed-on: https://git.genarrative.world/git/GenarrativeAI/Genarrative/pulls/386
Co-authored-by: Linghong <ink29535@proton.me>
Co-committed-by: Linghong <ink29535@proton.me>
2026-09-16 19:53:23 +08:00
suzmii fca3df964c Merge branch 'master' into fix/agc-canvas-json
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 5m15s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 4m31s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 4m21s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 5m1s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m39s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m48s
Project CI / Frontend tests (pull_request) Failing after 3m36s
Project CI / Repository checks (pull_request) Failing after 3m56s
Project CI / Native shell tests (pull_request) Successful in 6m40s
Project CI / AI game creator shell web tests (pull_request) Failing after 3m18s
Project CI / Backend tests (pull_request) Successful in 8m18s
2026-09-16 19:29:05 +08:00
lhk229 70157673b6 Merge branch 'master' into fix/ci
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 5m27s
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 5m46s
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 6m12s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 6m30s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m41s
Project CI / AI game creator shell Rust crates (push) Successful in 2m48s
Project CI / Frontend tests (push) Successful in 4m49s
Project CI / Repository checks (push) Successful in 3m43s
Project CI / Native shell tests (push) Successful in 6m41s
Project CI / Backend tests (push) Successful in 7m17s
Project CI / AI game creator shell web tests (push) Successful in 3m26s
2026-09-16 19:07:53 +08:00
suzmii ac8a51ca79 同步master最新基线并准备画布修复PR
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
合入平台会话与主线测试修复
保持画布交互、JSON识别和对话历史分页修复范围不变
2026-09-16 18:59:32 +08:00
kdletters 492e9e63af 合入 AGC 终止链路 abort 闪退修复
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 5m20s
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (push) Has been cancelled
codex_app_server 新增 spawn_codex_app_server_task,统一经 tauri::async_runtime::spawn 派发
终止链路、CodexThreadLease::drop、CodexTurnGuard::drop 三处不再在同步命令线程上 tokio::spawn
新增回归用例 codex_app_server_task_dispatch_needs_no_tokio_runtime_context
补记 tokio::spawn 导致 abort 闪退、cargo test 重写 generated 绑定两条踩坑
2026-09-16 18:58:19 +08:00
lhk229 57fae7037a 修复 Agent 异步测试等待
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 5m13s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 5m24s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 5m52s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 6m12s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m38s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m42s
Project CI / Frontend tests (pull_request) Successful in 4m28s
Project CI / Repository checks (pull_request) Successful in 3m39s
Project CI / Native shell tests (pull_request) Successful in 6m59s
Project CI / Backend tests (pull_request) Successful in 7m37s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m55s
移除不稳定的项目页文案前置

保留刷新 Agent 与拆解任务按钮就绪检查
2026-09-16 10:52:25 +00:00
suzmii 1b010fb1f8 修复DirectProject对话历史分页卡页
聊天历史按有效消息分页并返回原生游标,保留默认原始切片接口
工具和推理记录不占聊天页名额,逐行过滤避免传回大段工具输出
隔离重新加载和项目切换的迟到响应,补齐单飞、重试与消息去重
补充原生和真实App回归测试,增加人工只读日志重放入口
更新B05状态、规范与PR验收记录,不提交用户原始日志
2026-09-16 18:46:47 +08:00
kdletters 2bcfa10647 修复 AGC 客户端点「终止」整体闪退:同步命令线程改走 Tauri 全局异步 runtime
- codex_app_server 新增 spawn_codex_app_server_task,统一经 tauri::async_runtime::spawn 派发中断与收尾任务
- 终止链路 maybe_interrupt、CodexThreadLease::drop、CodexTurnGuard::drop 三处改走该入口,避免同步 Tauri 命令线程上 tokio::spawn panic 后跨 IPC 回调边界 abort
- 新增回归用例 codex_app_server_task_dispatch_needs_no_tokio_runtime_context,把派发入口钉在没有 tokio 上下文的线程上
- 同步 agc-skills manifest 指纹,修复 master 指纹回退导致的 skill-pack:check 失败(本地打包前置门禁)
- 记录两条踩坑:同步命令线程 tokio::spawn 导致客户端 abort 闪退、cargo test 重写 generated/*.ts 后前端 typecheck 必红
2026-09-16 18:40:14 +08:00
kdletters e777236817 补齐 decision-log 两处标题前缺失的空行
恢复引言引用块、2026-09-16 图标图集条目之前的空行分隔,与文件既有排版一致
仅新增两行空行,不改动任何决策条目内容
2026-09-16 18:35:52 +08:00
lhk229 d11c74212d Merge pull request '修复 AGC skill 包与 Agent 测试' (#387) from fix/ci into master
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 5m15s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 5m16s
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 5m38s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 6m8s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m45s
Project CI / AI game creator shell Rust crates (push) Successful in 2m28s
Project CI / Frontend tests (push) Failing after 3m36s
Project CI / Repository checks (push) Failing after 3m40s
Project CI / Native shell tests (push) Successful in 6m48s
Project CI / Backend tests (push) Successful in 7m47s
Project CI / AI game creator shell web tests (push) Failing after 3m44s
Reviewed-on: https://git.genarrative.world/git/GenarrativeAI/Genarrative/pulls/387
2026-09-16 18:33:52 +08:00
lhk229 82b2f853e7 修复 AGC skill 包与 Agent 测试
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Has been cancelled
同步工作流和试玩 skill 指纹

等待项目页面与拆解任务按钮就绪
2026-09-16 10:31:57 +00:00
kdletters 31f83a751a 三维请求自选技术栈,不再固定 Phaser
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 4m35s
Project CI / AI game creator shell Rust shard 1/4 (push) Failing after 5m18s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m32s
Project CI / AI game creator shell Rust shard 3/4 (push) Failing after 5m7s
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 3m54s
Project CI / AI game creator shell Rust crates (push) Successful in 2m26s
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
- DirectProject 系统提示新增三维请求合同:三维请求不受 Phaser 4.2.1 约束,由 Codex 自选三维技术栈
- 工程合同的 Phaser 固定约束按二维/三维分层,点名引擎的不匹配澄清规则保持不变
- 新增三维意图识别、三维回合合同与首页三维提示;伪 3D、等轴与代码话题不触发
- 补定向用例与里程碑/实施计划文档,并在 decision-log 记录该决策
2026-09-16 18:04:44 +08:00
lhk229 a1cafde7e9 修复 AI 创作壳测试稳定性
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 1/4 (push) Failing after 4m35s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m32s
Project CI / AI game creator shell Rust shard 3/4 (push) Failing after 4m48s
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 4m59s
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 5m37s
Project CI / AI game creator shell Rust crates (push) Successful in 2m42s
Project CI / Native shell tests (push) Failing after 2m47s
Project CI / Frontend tests (push) Failing after 3m29s
Project CI / AI game creator shell web tests (push) Failing after 49s
Project CI / Repository checks (push) Failing after 3m16s
Project CI / Backend tests (push) Successful in 7m7s
调整 Agent 项目打开测试的异步就绪等待

延长权限策略连续命令测试超时预算

同步 taonier-art-assets skill-pack 内容指纹

补充对应实施计划中的测试约定
2026-09-16 09:57:21 +00:00
suzmii 825ea76b7b 同步master最新变更
合入当前主线的多窗口与配置更新
保留AGC画布交互和JSON识别修复
保留双方新增的项目排障记录并解决文档冲突
2026-09-16 17:45:06 +08:00
kdletters 0594a90bdd 合入平台会话身份与凭据分离
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 4m15s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m32s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 4m58s
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 6m13s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 5m3s
Project CI / AI game creator shell Rust crates (push) Successful in 3m1s
Project CI / Frontend tests (push) Failing after 4m24s
Project CI / Repository checks (push) Successful in 4m1s
Project CI / Native shell tests (push) Successful in 7m45s
Project CI / AI game creator shell web tests (push) Failing after 3m4s
Project CI / Backend tests (push) Successful in 8m20s
平台会话快照拆分身份代次与写入 revision,凭据续期轮换不再中断在途生成
AGC Runner 请求参数新增 platform_auth_revision,并按 revision 做单调写入判定
解决与多窗口共享 Runner 的冲突,保留 claim adopt/publish 与界面参与锁语义
同步刷新轮换竞争、appSurface 鉴权用例与客户端 API 测试
补录平台会话身份与凭据分离的实施计划、共享记忆与决策记录
2026-09-16 17:42:00 +08:00
suzmii 910862fd06 修复AGC画布交互并识别UI设计JSON
稳定工作台与窗口标题栏状态同步,消除重复更新循环
支持画布右键平移并保留左键框选及资源拖动,完善中断清理
解耦运行不可用提示与资源选中状态
复用原生UI状态校验区分UI设计JSON和普通JSON,接入现有编辑器与代码预览
补充前端和原生回归测试、规范及待验收记录,明确对话历史分页尚未修复
2026-09-16 17:39:23 +08:00
kdletters aee862532c 删除已完成的多窗口里程碑临时文档
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 4m38s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 4m57s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m36s
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 5m52s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 4m42s
Project CI / AI game creator shell Rust crates (push) Successful in 2m39s
Project CI / Repository checks (push) Successful in 3m58s
Project CI / Frontend tests (push) Successful in 5m53s
Project CI / Native shell tests (push) Successful in 6m28s
Project CI / Backend tests (push) Successful in 7m49s
Project CI / AI game creator shell web tests (push) Successful in 2m8s
- 按规范驱动开发工作流,里程碑与实施计划属临时产物,合并后删除
- 持久结论已回写 AGC 技术方案 2026-09-16 节、共享记忆决策记录与踩坑记录
2026-09-16 17:15:22 +08:00
kdletters 42be8ea060 发布 AGC 标准版 0.1.47
- 版本号同步到 0.1.47:package.json / package-lock.json / tauri.conf.json / Cargo.toml / Cargo.lock
- 构建脚本读取 OSS 清单(已有 0.1.46)后自动递增,本版打包产物为 陶泥儿_0.1.47_x64-setup.exe
- 本轮同时包含同 AppData 多窗口共享 Agent Runner 的修复
2026-09-16 17:15:15 +08:00
kdletters 737a2266b9 支持同 AppData 多窗口共享 Agent Runner
- owner 独占锁改为可多窗口同时持有的界面参与锁 agent-runner.gui-participant.lock
- Runner 启动检查、attach 门禁与 watchdog 改用参与锁存活判定,最后一个窗口退出才关停 Runner
- 窗口启动只采纳现有 owner claim,登录/refresh/退出/换号才发布新 epoch claim
- 同 claim 重复 attach 改为幂等空操作,不再清空 Runner 平台登录态
- manifest 失效与 Runtime update relay 接收端改为按 token 去重的注册表并广播,失败只淘汰该接收端
- GUI 退出先释放参与锁,仍有其它窗口时保留 Runner 并记录 retained_for_other_windows
- Runner 启动失败时按最新 endpoint 复用一次,避免两窗口同时冷启动被实例锁竞争误报
- 更新 runner 定向测试、GUI 生命周期集成测试与自检用例
- 同步 AGC 技术方案、决策记录、踩坑记录与多窗口里程碑/实施计划
2026-09-16 16:56:21 +08:00
247 changed files with 16482 additions and 1336 deletions
@@ -32,7 +32,7 @@ Prefer `scripts/genarrative_external_api.py` for runnable REST calls. It uses on
- Use stable references such as `objectKey`, project resource ID, or asset ID where each operation permits them. Image edit/redraw is stricter: `sourceReferenceId` accepts only a registered project resource ID or asset ID; upload confirmation alone is not enough. Use `/assets/read-url` only for temporary preview/download access. - Use stable references such as `objectKey`, project resource ID, or asset ID where each operation permits them. Image edit/redraw is stricter: `sourceReferenceId` accepts only a registered project resource ID or asset ID; upload confirmation alone is not enough. Use `/assets/read-url` only for temporary preview/download access.
- Preserve both warning channels after completion. A general `warning` can coexist with `sliceWarning`; do not discard either. - Preserve both warning channels after completion. A general `warning` can coexist with `sliceWarning`; do not discard either.
- Do not invent missing derivatives. A source-preserved warning means the main source remains usable but requested post-processing failed. A slice warning means the complete transparent sheet is usable but individual slices are absent. - Do not invent missing derivatives. A source-preserved warning means the main source remains usable but requested post-processing failed. A slice warning means the complete transparent sheet is usable but individual slices are absent.
- Icon spritesheet generation accepts `sliceMode="connected-components"` (default alpha-connectivity detection) or `sliceMode="grid"`. Grid mode requires `gridX` and `gridY` (1-32); use `sliceCount` only to constrain connected-component output. - Icon spritesheet generation requires an explicit `sliceMode` and has no default. Use `sliceMode="grid"` with the `gridX` and `gridY` the requirement actually names (1-32 each) only for equal grid cells or fixed slots; use `sliceMode="connected-components"` for free-form sheets or an open number of subjects, and constrain the count with `sliceCount` instead of inventing grid dimensions. `connected-components` must not carry `gridX`/`gridY`; an omitted, contradictory, or misapplied declaration returns 400 before billing.
- For successful `style="pixelArt"`, treat completed-result and nested resource/asset dimensions as the final logical-grid PNG dimensions. They may differ from `size`, `imageSize`, the provider image, and `canvasCompletion.placeholder`; do not rescale or reject the artifact to match those inputs. - For successful `style="pixelArt"`, treat completed-result and nested resource/asset dimensions as the final logical-grid PNG dimensions. They may differ from `size`, `imageSize`, the provider image, and `canvasCompletion.placeholder`; do not rescale or reject the artifact to match those inputs.
- Keep generated artifacts in the canvas and asset library together. Character animation accepts `assetFolderId` and `assetLabel`; its completed result directly returns the final `assetKind="character-animation"` resource and asset with formal sequence fields. Do not create a duplicate first-frame record. - Keep generated artifacts in the canvas and asset library together. Character animation accepts `assetFolderId` and `assetLabel`; its completed result directly returns the final `assetKind="character-animation"` resource and asset with formal sequence fields. Do not create a duplicate first-frame record.
@@ -94,7 +94,7 @@ For image edit/redraw, confirming an upload is not sufficient: create a project
The icon-spritesheet primary `referenceId` is intentionally stricter than ordinary image references: it accepts only a current-owner project resource ID or asset ID whose authoritative `assetKind` is `icon-spec`. It does not accept an `objectKey`, URL, Data URL, or Blob URL. The icon-spritesheet primary `referenceId` is intentionally stricter than ordinary image references: it accepts only a current-owner project resource ID or asset ID whose authoritative `assetKind` is `icon-spec`. It does not accept an `objectKey`, URL, Data URL, or Blob URL.
`sliceMode` controls atlas splitting. Use `"connected-components"` (default) to detect independent opaque regions by alpha connectivity, or `"grid"` with positive `gridX` and `gridY` values (maximum 32 each). `sliceCount` optionally constrains the connected-component result. `sliceMode` is required and has no default, so every request must state it. Use `"connected-components"` to detect independent opaque regions by alpha connectivity, or `"grid"` with positive `gridX` and `gridY` values (maximum 32 each) only when the requirement names equal grid cells or fixed slots; the dimensions must come from that requirement. `connected-components` must not carry `gridX`/`gridY`, and `sliceCount` constrains the connected-component result instead of expressing a grid. Omitting `sliceMode`, or contradicting the declared mode with grid dimensions, returns 400 before pricing, enqueueing, or any provider call.
## Common Values ## Common Values
@@ -79,9 +79,9 @@ Keep the existing autonomous-build task graph. Do not add a parallel task system
1. `art-director` generates `assets/art-spec.png` with image generation, `kind: "spec"`, then registers it as `assetKind: "icon-spec"`. This image is the authoritative visual spec; `generationInputs.artSpec` is supporting structured context. 1. `art-director` generates `assets/art-spec.png` with image generation, `kind: "spec"`, then registers it as `assetKind: "icon-spec"`. This image is the authoritative visual spec; `generationInputs.artSpec` is supporting structured context.
2. `design-foundation` generates `assets/ui-prototype.png` with `kind: "ui-design"`, using the registered art-spec resource ID in `referenceImageSrcs`. 2. `design-foundation` generates `assets/ui-prototype.png` with `kind: "ui-design"`, using the registered art-spec resource ID in `referenceImageSrcs`.
3. `art-asset-plan` generates transparent `assets/art-spritesheet.png` through icon spritesheet generation, using the same registered art-spec resource ID as `referenceId` plus concrete `iconDescriptions`. For a fixed four-category game contract it may send `sliceMode: "grid"`; for free-form assets use `sliceMode: "connected-components"` (the default). 3. `art-asset-plan` generates transparent `assets/art-spritesheet.png` through icon spritesheet generation, using the same registered art-spec resource ID as `referenceId` plus concrete `iconDescriptions`. `sliceMode` is required and has no default: send `sliceMode: "grid"` with `gridX`/`gridY` only when the requirement itself fixes the slots or names the column/row count, and otherwise send `sliceMode: "connected-components"` (with `sliceCount` when a subject count must be constrained); never invent a grid to express "kinds of assets", and never send `gridX`/`gridY` with `connected-components`.
For a playable Canvas game, do not stop at generation. Make `code-prototype` depend on `art-asset-plan` and consume the persisted `iconImageSrcs` slices for core players, blocks or targets, scene obstacles, and feedback. When using the fixed four-category contract, require response `sliceMode: "grid"` and exactly four slices before registering the local runtime sheet; both fewer and extra components fail closed. Treat `art-spec.png` as reference-only. A full-sheet `<img>`, CSS background, path-only mention, guessed equal-grid crop, or code-drawn replacement for core entities is not runtime asset use. If slicing produces `sliceWarning`, keep the complete transparent sheet as a valid editor artifact, but fail the playable game asset gate until real slice files or verified atlas coordinates exist; never invent coordinates or replace the icon-spritesheet route with ordinary image generation. For a playable Canvas game, do not stop at generation. Make `code-prototype` depend on `art-asset-plan` and consume the persisted `iconImageSrcs` slices for core players, blocks or targets, scene obstacles, and feedback. When the requirement fixes grid slots, require the response `sliceMode` to match the declared `grid` request and exactly `gridX × gridY` slices before registering the local runtime sheet; a connected-components request is instead judged by its own `sliceCount` or by the requirement, and both fewer and extra components fail closed. Treat `art-spec.png` as reference-only. A full-sheet `<img>`, CSS background, path-only mention, guessed equal-grid crop, or code-drawn replacement for core entities is not runtime asset use. If slicing produces `sliceWarning`, keep the complete transparent sheet as a valid editor artifact, but fail the playable game asset gate until real slice files or verified atlas coordinates exist; never invent coordinates or replace the icon-spritesheet route with ordinary image generation.
Never use `assets/ui-prototype.png` as the spritesheet visual-spec reference. UI extraction is outside this canonical DAG. Never use `assets/ui-prototype.png` as the spritesheet visual-spec reference. UI extraction is outside this canonical DAG.
@@ -617,6 +617,7 @@ class GenarrativeExternalClient:
self, self,
reference_id: str, reference_id: str,
icon_descriptions: list[str], icon_descriptions: list[str],
slice_mode: str,
**fields: Any, **fields: Any,
) -> Any: ) -> Any:
reference_id = normalize_optional_text(reference_id) reference_id = normalize_optional_text(reference_id)
@@ -625,6 +626,20 @@ class GenarrativeExternalClient:
descriptions = [item.strip() for item in icon_descriptions if item.strip()] descriptions = [item.strip() for item in icon_descriptions if item.strip()]
if not descriptions: if not descriptions:
raise GenarrativeApiError("icon_descriptions must contain at least one non-empty item") raise GenarrativeApiError("icon_descriptions must contain at least one non-empty item")
slice_mode = normalize_optional_text(slice_mode)
if slice_mode not in ("connected-components", "grid"):
raise GenarrativeApiError(
"slice_mode must be declared explicitly as 'connected-components' or 'grid'; the API has no default"
)
grid_x = fields.get("gridX")
grid_y = fields.get("gridY")
if slice_mode == "grid":
if grid_x is None or grid_y is None:
raise GenarrativeApiError("slice_mode='grid' requires both gridX and gridY")
elif grid_x is not None or grid_y is not None:
raise GenarrativeApiError(
"slice_mode='connected-components' must not carry gridX/gridY"
)
label = fields.get("assetLabel", "图标图集") label = fields.get("assetLabel", "图标图集")
self._apply_canvas_session_fields(fields, label, 1024, 1024) self._apply_canvas_session_fields(fields, label, 1024, 1024)
fields.setdefault("screenColor", "auto") fields.setdefault("screenColor", "auto")
@@ -635,6 +650,7 @@ class GenarrativeExternalClient:
**fields, **fields,
"referenceId": reference_id, "referenceId": reference_id,
"iconDescriptions": descriptions, "iconDescriptions": descriptions,
"sliceMode": slice_mode,
}, },
idempotency_key=idempotency_key, idempotency_key=idempotency_key,
) )
@@ -879,9 +895,9 @@ def _self_test() -> None:
client.generate_icon_spritesheet( client.generate_icon_spritesheet(
"editor-resource-spec", "editor-resource-spec",
["蛇头向上", "蛇身直线", "转角", "尾部", "四类食物"], ["蛇头向上", "蛇身直线", "转角", "尾部", "四类食物"],
"connected-components",
canvasSession=session, canvasSession=session,
assetLabel="贪吃蛇透明图集", assetLabel="贪吃蛇透明图集",
sliceMode="connected-components",
referenceId="must-not-override-explicit-reference", referenceId="must-not-override-explicit-reference",
iconDescriptions=["不得覆盖显式图标描述"], iconDescriptions=["不得覆盖显式图标描述"],
) )
+9
View File
@@ -158,6 +158,15 @@ ALIYUN_OSS_POST_EXPIRE_SECONDS="600"
ALIYUN_OSS_POST_MAX_SIZE_BYTES="20971520" ALIYUN_OSS_POST_MAX_SIZE_BYTES="20971520"
ALIYUN_OSS_SUCCESS_ACTION_STATUS="200" ALIYUN_OSS_SUCCESS_ACTION_STATUS="200"
# AGC 项目定时快照上传目标。对象只落在服务端私有前缀
# `agc/project-snapshots/v1/{user}/{project}/` 下,客户端直传票据不覆盖该前缀。
# bucket 与凭据可以与资源 bucket 分离;凭据未设置时回退使用 ALIYUN_OSS_ACCESS_KEY_*
# 但 bucket / endpoint 默认指向 AGC 发行 bucket,需要该凭据具备目标 bucket 的 PutObject 权限。
GENARRATIVE_AGC_PROJECT_SNAPSHOT_OSS_BUCKET="agc-dev"
GENARRATIVE_AGC_PROJECT_SNAPSHOT_OSS_ENDPOINT="oss-rg-china-mainland.aliyuncs.com"
GENARRATIVE_AGC_PROJECT_SNAPSHOT_OSS_ACCESS_KEY_ID=""
GENARRATIVE_AGC_PROJECT_SNAPSHOT_OSS_ACCESS_KEY_SECRET=""
# BgFilter 受限资源 worker。父 api-server / external-generation-worker 与唯一的 # BgFilter 受限资源 worker。父 api-server / external-generation-worker 与唯一的
# `GENARRATIVE_PROCESS_ROLE=bgfilter-worker` 进程必须使用同一个内部 Token。 # `GENARRATIVE_PROCESS_ROLE=bgfilter-worker` 进程必须使用同一个内部 Token。
# `npm run dev` 与 `npm run dev:api-server` 都会自动带起并验活唯一 worker,不要再开第二个终端重复启动。 # `npm run dev` 与 `npm run dev:api-server` 都会自动带起并验活唯一 worker,不要再开第二个终端重复启动。
+4
View File
@@ -7,6 +7,10 @@ on:
pull_request: pull_request:
workflow_dispatch: workflow_dispatch:
concurrency:
group: project-ci-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions: permissions:
contents: read contents: read
@@ -38,6 +38,7 @@ vi.mock('../api/adminApiClient', () => ({
interface MockIntersectionObserverController { interface MockIntersectionObserverController {
enter: (target: Element) => void; enter: (target: Element) => void;
enterAll: (targets: Element[]) => void;
isObserved: (target: Element) => boolean; isObserved: (target: Element) => boolean;
} }
@@ -106,6 +107,25 @@ function installIntersectionObserverMock(): MockIntersectionObserverController {
); );
}); });
}, },
enterAll(targets) {
act(() => {
for (const target of targets) {
const record = observed.get(target);
if (!record) {
throw new Error('目标缩略图尚未进入 IntersectionObserver');
}
record.callback(
[
{
isIntersecting: true,
target,
} as IntersectionObserverEntry,
],
record.observer,
);
}
});
},
isObserved(target) { isObserved(target) {
return observed.has(target); return observed.has(target);
}, },
@@ -753,10 +773,10 @@ test('后台素材查询为大量同时可见的缩略图持续错峰换签', as
const thumbnails = entries.map((entry) => const thumbnails = entries.map((entry) =>
thumbnailElementForLabel(entry.label), thumbnailElementForLabel(entry.label),
); );
thumbnails.forEach((thumbnail) => { for (const thumbnail of thumbnails) {
expect(observer.isObserved(thumbnail)).toBe(true); expect(observer.isObserved(thumbnail)).toBe(true);
observer.enter(thumbnail); }
}); observer.enterAll(thumbnails);
await act(async () => { await act(async () => {
await Promise.resolve(); await Promise.resolve();
}); });
@@ -776,7 +796,7 @@ test('后台素材查询为大量同时可见的缩略图持续错峰换签', as
await vi.advanceTimersByTimeAsync(200); await vi.advanceTimersByTimeAsync(200);
}); });
expect(getAdminAssetReadUrl).toHaveBeenCalledTimes(105); expect(getAdminAssetReadUrl).toHaveBeenCalledTimes(105);
}); }, 10_000);
test('后台素材查询读取更多后为新进入可视区域的素材换签', async () => { test('后台素材查询读取更多后为新进入可视区域的素材换签', async () => {
const observer = installIntersectionObserverMock(); const observer = installIntersectionObserverMock();
@@ -15,7 +15,7 @@
"autoCompactTokenLimit": 64000, "autoCompactTokenLimit": 64000,
"toolOutputTokenLimit": 12000, "toolOutputTokenLimit": 12000,
"requestTimeoutMs": 180000, "requestTimeoutMs": 180000,
"maxRetries": 2, "maxRetries": 10,
"retryBackoffMs": 500 "retryBackoffMs": 500
}, },
"agentLlm": {} "agentLlm": {}
+4 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "@genarrative/ai-game-creator-shell", "name": "@genarrative/ai-game-creator-shell",
"private": true, "private": true,
"version": "0.1.45", "version": "0.1.47",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "node scripts/start-tauri-dev.mjs", "dev": "node scripts/start-tauri-dev.mjs",
@@ -47,6 +47,7 @@
"@tauri-apps/plugin-dialog": "^2.7.2", "@tauri-apps/plugin-dialog": "^2.7.2",
"@tauri-apps/plugin-http": "^2.5.9", "@tauri-apps/plugin-http": "^2.5.9",
"@tauri-apps/plugin-opener": "~2", "@tauri-apps/plugin-opener": "~2",
"@tauri-apps/plugin-updater": "2.11.0",
"@vitejs/plugin-react": "^5.0.4", "@vitejs/plugin-react": "^5.0.4",
"focus-trap-react": "^12.0.3", "focus-trap-react": "^12.0.3",
"lexical": "^0.47.0", "lexical": "^0.47.0",
@@ -57,6 +58,7 @@
"react-colorful": "^5.8.0", "react-colorful": "^5.8.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-markdown": "^10.1.0", "react-markdown": "^10.1.0",
"react-window": "^1.8.11",
"rehype-highlight": "^7.0.2", "rehype-highlight": "^7.0.2",
"remark-gfm": "^4.0.1", "remark-gfm": "^4.0.1",
"vite": "^6.2.0", "vite": "^6.2.0",
@@ -70,6 +72,7 @@
"@testing-library/user-event": "^14.6.1", "@testing-library/user-event": "^14.6.1",
"@types/react": "^19.2.14", "@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@types/react-window": "^1.8.8",
"tailwindcss": "^4.1.14", "tailwindcss": "^4.1.14",
"typescript": "~5.8.2", "typescript": "~5.8.2",
"vitest": "^0.34.6" "vitest": "^0.34.6"
@@ -1,6 +1,7 @@
import { spawnSync } from 'node:child_process'; import { spawnSync } from 'node:child_process';
import { createHash } from 'node:crypto'; import { createHash } from 'node:crypto';
import fs from 'node:fs'; import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path'; import path from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
@@ -28,14 +29,30 @@ const cargoManifestPath = path.join(appRoot, 'src-tauri', 'Cargo.toml');
const cargoLockPath = path.join(appRoot, 'src-tauri', 'Cargo.lock'); const cargoLockPath = path.join(appRoot, 'src-tauri', 'Cargo.lock');
const defaultOssBaseUrl = const defaultOssBaseUrl =
'https://agc-dev.oss-rg-china-mainland.aliyuncs.com/agc'; 'https://agc-dev.oss-rg-china-mainland.aliyuncs.com/agc';
const updateManifestUrl =
process.env.VITE_AGC_UPDATE_MANIFEST_URL?.trim() || /**
`${process.env.AGC_UPDATE_OSS_BASE_URL?.trim() || defaultOssBaseUrl}/latest.json`; * 发布渠道 → 目标平台。渠道名会进入 OSS 路径并烘焙进客户端端点,
* 一旦发布就不能改名(改名等于已发布客户端再也找不到更新)。
*/
const releaseChannels = {
'dev-win': 'windows',
'dev-mac': 'darwin',
};
function ossBaseUrl() {
return (
process.env.AGC_UPDATE_OSS_BASE_URL?.trim() || defaultOssBaseUrl
).replace(/\/+$/u, '');
}
function readPackageJson() { function readPackageJson() {
return JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); return JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
} }
function readReleaseNotes() {
return process.env.AGC_UPDATE_RELEASE_NOTES?.trim() || '';
}
export function compareVersions(left, right) { export function compareVersions(left, right) {
const leftParts = left.split('.').map(Number); const leftParts = left.split('.').map(Number);
const rightParts = right.split('.').map(Number); const rightParts = right.split('.').map(Number);
@@ -66,26 +83,87 @@ export function nextPatchVersion(localVersion, remoteVersion) {
return `${major}.${minor}.${patch + 1}`; return `${major}.${minor}.${patch + 1}`;
} }
async function readRemoteVersion() { export function resolveReleasePlatform(target = releaseTarget) {
if (target.includes('windows')) return 'windows';
if (target.includes('apple-darwin')) return 'darwin';
if (target.includes('linux')) return 'linux';
throw new Error(`不支持的发布目标:${target}`);
}
export function resolveReleaseChannel(
env = process.env,
target = releaseTarget,
) {
const platform = resolveReleasePlatform(target);
const requested = env.AGC_UPDATE_CHANNEL?.trim();
if (requested) {
const channelPlatform = releaseChannels[requested];
if (!channelPlatform) {
throw new Error(
`未知发布渠道 ${requested};当前支持:${Object.keys(releaseChannels).join('、')}`,
);
}
if (channelPlatform !== platform) {
throw new Error(
`渠道 ${requested} 只能用于 ${channelPlatform} 目标,当前构建目标为 ${target}`,
);
}
return requested;
}
const defaultChannel = Object.entries(releaseChannels).find(
([, channelPlatform]) => channelPlatform === platform,
)?.[0];
if (!defaultChannel) {
throw new Error(
`目标 ${target} 没有默认发布渠道,请显式设置 AGC_UPDATE_CHANNEL`,
);
}
return defaultChannel;
}
export function updateManifestUrl(channel = resolveReleaseChannel()) {
return `${ossBaseUrl()}/${channel}/latest.json`;
}
/**
* 更新插件按运行时平台键查找清单条目:universal macOS 包同时挂
* `darwin-aarch64` 与 `darwin-x86_64`,单架构目标只挂对应键。
*/
export function resolveManifestPlatformKeys(target = releaseTarget) {
if (target === 'universal-apple-darwin') {
return ['darwin-aarch64', 'darwin-x86_64'];
}
if (target === 'aarch64-apple-darwin') return ['darwin-aarch64'];
if (target === 'x86_64-apple-darwin') return ['darwin-x86_64'];
if (target.includes('windows')) {
return [
target.startsWith('aarch64') ? 'windows-aarch64' : 'windows-x86_64',
];
}
throw new Error(`不支持的发布目标:${target}`);
}
async function readRemoteVersion(channel = resolveReleaseChannel()) {
const manifestUrl = updateManifestUrl(channel);
let response; let response;
try { try {
response = await fetch(updateManifestUrl, { response = await fetch(manifestUrl, {
headers: { Accept: 'application/json' }, headers: { Accept: 'application/json' },
}); });
} catch (error) { } catch (error) {
throw new Error(`读取 OSS 版本清单失败:${error.message}`); throw new Error(`读取 OSS 渠道清单失败:${error.message}`);
} }
if (response.status === 404) return null; if (response.status === 404) return null;
if (!response.ok) { if (!response.ok) {
throw new Error(`读取 OSS 版本清单失败:HTTP ${response.status}`); throw new Error(`读取 OSS 渠道清单失败:HTTP ${response.status}`);
} }
let manifest; let manifest;
try { try {
manifest = await response.json(); manifest = await response.json();
} catch (error) { } catch (error) {
throw new Error(`OSS 版本清单不是有效 JSON${error.message}`); throw new Error(`OSS 渠道清单不是有效 JSON${error.message}`);
} }
return parseVersion(manifest?.version, 'OSS版本清单 version'); return parseVersion(manifest?.version, 'OSS渠道清单 version');
} }
function replaceVersionLine(source, version, pattern, label) { function replaceVersionLine(source, version, pattern, label) {
@@ -94,8 +172,9 @@ function replaceVersionLine(source, version, pattern, label) {
} }
export async function prepareReleaseVersion() { export async function prepareReleaseVersion() {
const channel = resolveReleaseChannel();
const localVersion = parseVersion(readPackageJson().version, '本地版本'); const localVersion = parseVersion(readPackageJson().version, '本地版本');
const remoteVersion = await readRemoteVersion(); const remoteVersion = await readRemoteVersion(channel);
const requestedVersion = process.env.AGC_RELEASE_VERSION?.trim(); const requestedVersion = process.env.AGC_RELEASE_VERSION?.trim();
const nextVersion = requestedVersion const nextVersion = requestedVersion
? parseVersion(requestedVersion, '指定版本') ? parseVersion(requestedVersion, '指定版本')
@@ -158,8 +237,8 @@ export async function prepareReleaseVersion() {
console.log( console.log(
requestedVersion requestedVersion
? `[ai-game-creator-shell] 使用指定版本 ${nextVersion}(本地 ${localVersion} / OSS ${remoteVersion ?? '不存在'}` ? `[ai-game-creator-shell] 渠道 ${channel} 使用指定版本 ${nextVersion}(本地 ${localVersion} / OSS ${remoteVersion ?? '不存在'}`
: `[ai-game-creator-shell] 版本 ${localVersion} / OSS ${remoteVersion ?? '不存在'} -> ${nextVersion}`, : `[ai-game-creator-shell] 渠道 ${channel} 版本 ${localVersion} / OSS ${remoteVersion ?? '不存在'} -> ${nextVersion}`,
); );
return nextVersion; return nextVersion;
} }
@@ -187,18 +266,43 @@ export function buildTauriBuildArguments(
]; ];
} }
/** 渠道端点必须由构建期注入:官方更新插件的端点配置不支持运行期改渠道。 */
export function createChannelConfig(channel = resolveReleaseChannel()) {
return {
plugins: {
updater: {
endpoints: [updateManifestUrl(channel)],
},
},
};
}
function writeChannelConfigFile(channel) {
const configPath = path.join(
os.tmpdir(),
`agc-tauri-channel-${channel}.json`,
);
fs.writeFileSync(
configPath,
`${JSON.stringify(createChannelConfig(channel), null, 2)}\n`,
);
return configPath;
}
export function runTauriBuild(args = []) { export function runTauriBuild(args = []) {
const tauriArguments = buildTauriBuildArguments(args);
if (!tauriArguments.includes('--config') && !tauriArguments.includes('-c')) {
const channel = resolveReleaseChannel();
const configPath = writeChannelConfigFile(channel);
console.log(
`[ai-game-creator-shell] 渠道 ${channel} 端点配置:${configPath}`,
);
tauriArguments.push('--config', configPath);
}
const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm'; const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
const result = spawnSync( const result = spawnSync(
npmCommand, npmCommand,
[ ['--prefix', '../..', 'exec', 'tauri', '--', ...tauriArguments],
'--prefix',
'../..',
'exec',
'tauri',
'--',
...buildTauriBuildArguments(args),
],
{ cwd: appRoot, stdio: 'inherit', shell: process.platform === 'win32' }, { cwd: appRoot, stdio: 'inherit', shell: process.platform === 'win32' },
); );
if (result.error) throw result.error; if (result.error) throw result.error;
@@ -216,10 +320,14 @@ function listFiles(root) {
function artifactPriority(filePath) { function artifactPriority(filePath) {
const name = path.basename(filePath).toLowerCase(); const name = path.basename(filePath).toLowerCase();
if (releaseTarget.includes('windows')) return name.endsWith('.exe') ? 0 : 99; if (releaseTarget.includes('windows')) return name.endsWith('.exe') ? 0 : 99;
if (process.platform === 'darwin') return name.endsWith('.dmg') ? 0 : 99; // 更新链路要的是 updater 产物(macOS 为 .app.tar.gz),dmg 只作人工分发。
if (name.endsWith('.appimage')) return 0; if (releaseTarget.includes('apple-darwin')) {
if (name.endsWith('.deb')) return 1; return name.endsWith('.app.tar.gz') ? 0 : 99;
if (name.endsWith('.rpm')) return 2; }
if (name.endsWith('.appimage.tar.gz')) return 0;
if (name.endsWith('.appimage')) return 1;
if (name.endsWith('.deb')) return 2;
if (name.endsWith('.rpm')) return 3;
return 99; return 99;
} }
@@ -242,36 +350,100 @@ export function selectReleaseArtifact(files) {
); );
} }
export function createUpdateManifest(artifactPath) { function readUpdaterSignature(artifactPath) {
const signaturePath = `${artifactPath}.sig`;
if (!fs.existsSync(signaturePath)) {
throw new Error(
`缺少更新包签名:${signaturePath};需要 bundle.createUpdaterArtifacts 与签名私钥(TAURI_SIGNING_PRIVATE_KEY / TAURI_SIGNING_PRIVATE_KEY_PATH`,
);
}
const signature = fs.readFileSync(signaturePath, 'utf8').trim();
if (!signature) throw new Error(`更新包签名为空:${signaturePath}`);
return signature;
}
export function createUpdateManifest(
artifactPath,
{
channel = resolveReleaseChannel(),
target = releaseTarget,
publishedAt = new Date().toISOString(),
} = {},
) {
const signature = readUpdaterSignature(artifactPath);
const version = readPackageJson().version;
const fileName = path.basename(artifactPath);
const url = `${ossBaseUrl()}/${channel}/${encodeURIComponent(version)}/${encodeURIComponent(fileName)}`;
const platforms = {};
for (const key of resolveManifestPlatformKeys(target)) {
platforms[key] = { signature, url };
}
const notes = readReleaseNotes();
return {
version,
...(notes ? { notes } : {}),
pub_date: publishedAt,
platforms,
};
}
/** 旧协议(sha256)清单:只用于把已发布客户端带到新渠道协议,一个版本周期后整条删除。 */
export function createLegacyUpdateManifest(
artifactPath,
{ channel = resolveReleaseChannel() } = {},
) {
const bytes = fs.readFileSync(artifactPath); const bytes = fs.readFileSync(artifactPath);
const version = readPackageJson().version; const version = readPackageJson().version;
const fileName = path.basename(artifactPath); const fileName = path.basename(artifactPath);
const baseUrl = ( const notes = readReleaseNotes();
process.env.AGC_UPDATE_OSS_BASE_URL?.trim() || defaultOssBaseUrl
).replace(/\/+$/u, '');
const encodedFileName = encodeURIComponent(fileName).replace(/%2F/giu, '/');
return { return {
version, version,
downloadUrl: `${baseUrl}/${encodeURIComponent(version)}/${encodedFileName}`, downloadUrl: `${ossBaseUrl()}/${channel}/${encodeURIComponent(version)}/${encodeURIComponent(fileName)}`,
sha256: createHash('sha256').update(bytes).digest('hex'), sha256: createHash('sha256').update(bytes).digest('hex'),
size: bytes.length, size: bytes.length,
...(process.env.AGC_UPDATE_RELEASE_NOTES?.trim() ...(notes ? { releaseNotes: notes } : {}),
? { releaseNotes: process.env.AGC_UPDATE_RELEASE_NOTES.trim() }
: {}),
}; };
} }
export function generateUpdateManifest() { export function generateUpdateManifest() {
const channel = resolveReleaseChannel();
const artifact = selectReleaseArtifact(listFiles(bundleRoot)); const artifact = selectReleaseArtifact(listFiles(bundleRoot));
if (!artifact) { if (!artifact) {
throw new Error(`未找到可发布的 AGC 安装包:${bundleRoot}`); throw new Error(`未找到可发布的 AGC 安装包:${bundleRoot}`);
} }
const manifest = createUpdateManifest(artifact); const manifest = createUpdateManifest(artifact, { channel });
const manifestPath = path.join(bundleRoot, 'latest.json'); const manifestPath = path.join(bundleRoot, 'latest.json');
fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`); fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
console.log(`[ai-game-creator-shell] 已生成 ${manifestPath}`); const legacyManifest =
channel === 'dev-win'
? createLegacyUpdateManifest(artifact, { channel })
: null;
const legacyManifestPath = legacyManifest
? path.join(bundleRoot, 'legacy-latest.json')
: null;
if (legacyManifest && legacyManifestPath) {
fs.writeFileSync(
legacyManifestPath,
`${JSON.stringify(legacyManifest, null, 2)}\n`,
);
}
console.log(
`[ai-game-creator-shell] 渠道 ${channel}:已生成 ${manifestPath}`,
);
console.log(`[ai-game-creator-shell] 安装包:${artifact}`); console.log(`[ai-game-creator-shell] 安装包:${artifact}`);
return { artifact, manifestPath, manifest }; if (legacyManifestPath) {
console.log(
`[ai-game-creator-shell] 旧协议迁移清单:${legacyManifestPath}`,
);
}
return {
channel,
artifact,
manifest,
manifestPath,
legacyManifest,
legacyManifestPath,
};
} }
if ( if (
@@ -1,24 +1,61 @@
import assert from 'node:assert/strict'; import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs'; import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { test } from 'node:test'; import { test } from 'node:test';
import { fileURLToPath } from 'node:url';
import { import {
compareVersions, compareVersions,
createChannelConfig,
createLegacyUpdateManifest,
createUpdateManifest, createUpdateManifest,
nextPatchVersion, nextPatchVersion,
resolveManifestPlatformKeys,
resolveReleaseChannel,
selectReleaseArtifact, selectReleaseArtifact,
updateManifestUrl,
} from './build-release.mjs'; } from './build-release.mjs';
test('selects an explicit release artifact when configured', () => { const windowsTarget = 'x86_64-pc-windows-msvc';
const artifactPath = new URL('../package.json', import.meta.url).pathname; const universalTarget = 'universal-apple-darwin';
const previous = process.env.AGC_UPDATE_ARTIFACT;
process.env.AGC_UPDATE_ARTIFACT = artifactPath; function withEnv(overrides, run) {
try { const previous = new Map();
assert.equal(selectReleaseArtifact([]), artifactPath); for (const [key, value] of Object.entries(overrides)) {
} finally { previous.set(key, process.env[key]);
if (previous === undefined) delete process.env.AGC_UPDATE_ARTIFACT; if (value === undefined) delete process.env[key];
else process.env.AGC_UPDATE_ARTIFACT = previous; else process.env[key] = value;
} }
try {
return run();
} finally {
for (const [key, value] of previous) {
if (value === undefined) delete process.env[key];
else process.env[key] = value;
}
}
}
function withSignedArtifact(fileName, run) {
const directory = mkdtempSync(path.join(os.tmpdir(), 'agc-release-test-'));
try {
const artifact = path.join(directory, fileName);
writeFileSync(artifact, 'installation package');
writeFileSync(`${artifact}.sig`, 'signature-content\n');
return run(artifact);
} finally {
rmSync(directory, { recursive: true, force: true });
}
}
test('selects an explicit release artifact when configured', () => {
const artifactPath = fileURLToPath(
new URL('../package.json', import.meta.url),
);
withEnv({ AGC_UPDATE_ARTIFACT: artifactPath }, () => {
assert.equal(selectReleaseArtifact([]), artifactPath);
});
}); });
test('does not select unsupported files', () => { test('does not select unsupported files', () => {
@@ -28,47 +65,123 @@ test('does not select unsupported files', () => {
); );
}); });
test('manifest contains version, download URL and integrity fields', () => { test('resolves the channel from the target platform and rejects mismatches', () => {
const manifest = createUpdateManifest( assert.equal(resolveReleaseChannel({}, windowsTarget), 'dev-win');
new URL('../package.json', import.meta.url).pathname, assert.equal(resolveReleaseChannel({}, universalTarget), 'dev-mac');
assert.equal(
resolveReleaseChannel({ AGC_UPDATE_CHANNEL: 'dev-mac' }, universalTarget),
'dev-mac',
); );
assert.match(manifest.version, /^\d+\.\d+\.\d+$/u); assert.throws(
assert.match( () =>
manifest.downloadUrl, resolveReleaseChannel({ AGC_UPDATE_CHANNEL: 'dev-mac' }, windowsTarget),
new RegExp(`/agc/${manifest.version}/package\\.json$`, 'u'), /只能用于 darwin 目标/u,
);
assert.throws(
() =>
resolveReleaseChannel({ AGC_UPDATE_CHANNEL: 'beta-win' }, windowsTarget),
/未知发布渠道/u,
); );
assert.equal(manifest.sha256.length, 64);
assert.equal(typeof manifest.size, 'number');
}); });
test('manifest preserves multiline release notes', () => { test('channel manifest URL and build-time endpoint follow the channel', () => {
const previous = process.env.AGC_UPDATE_RELEASE_NOTES; withEnv({ AGC_UPDATE_OSS_BASE_URL: undefined }, () => {
process.env.AGC_UPDATE_RELEASE_NOTES = '第一行\n第二行\r\n第三行'; assert.equal(
try { updateManifestUrl('dev-win'),
const manifest = createUpdateManifest( 'https://agc-dev.oss-rg-china-mainland.aliyuncs.com/agc/dev-win/latest.json',
new URL('../package.json', import.meta.url).pathname, );
assert.deepEqual(createChannelConfig('dev-mac'), {
plugins: {
updater: {
endpoints: [
'https://agc-dev.oss-rg-china-mainland.aliyuncs.com/agc/dev-mac/latest.json',
],
},
},
});
});
});
test('universal macOS builds publish one artifact under both platform keys', () => {
assert.deepEqual(resolveManifestPlatformKeys(universalTarget), [
'darwin-aarch64',
'darwin-x86_64',
]);
assert.deepEqual(resolveManifestPlatformKeys(windowsTarget), [
'windows-x86_64',
]);
});
test('channel manifest carries version, platform keys and signature', () => {
withSignedArtifact('陶泥儿_0.1.48_x64-setup.exe', (artifact) => {
withEnv({ AGC_UPDATE_RELEASE_NOTES: '修复与改进' }, () => {
const manifest = createUpdateManifest(artifact, {
channel: 'dev-win',
target: windowsTarget,
publishedAt: '2026-09-17T00:00:00.000Z',
});
assert.match(manifest.version, /^\d+\.\d+\.\d+$/u);
assert.equal(manifest.notes, '修复与改进');
assert.equal(manifest.pub_date, '2026-09-17T00:00:00.000Z');
assert.deepEqual(Object.keys(manifest.platforms), ['windows-x86_64']);
assert.equal(
manifest.platforms['windows-x86_64'].signature,
'signature-content',
);
assert.match(
manifest.platforms['windows-x86_64'].url,
new RegExp(`/agc/dev-win/${manifest.version}/`, 'u'),
);
});
});
});
test('missing signature fails the channel manifest closed', () => {
const directory = mkdtempSync(path.join(os.tmpdir(), 'agc-release-test-'));
try {
const artifact = path.join(directory, '陶泥儿_0.1.48_x64-setup.exe');
writeFileSync(artifact, 'installation package');
assert.throws(
() =>
createUpdateManifest(artifact, {
channel: 'dev-win',
target: windowsTarget,
}),
/缺少更新包签名/u,
); );
assert.equal(manifest.releaseNotes, '第一行\n第二行\r\n第三行');
} finally { } finally {
if (previous === undefined) delete process.env.AGC_UPDATE_RELEASE_NOTES; rmSync(directory, { recursive: true, force: true });
else process.env.AGC_UPDATE_RELEASE_NOTES = previous;
} }
}); });
test('next release version follows the higher local or OSS version', () => { test('legacy manifest keeps the sha256 contract of published clients', () => {
withSignedArtifact('陶泥儿_0.1.48_x64-setup.exe', (artifact) => {
const legacy = createLegacyUpdateManifest(artifact, {
channel: 'dev-win',
});
assert.match(legacy.version, /^\d+\.\d+\.\d+$/u);
assert.equal(legacy.sha256.length, 64);
assert.equal(legacy.size, 'installation package'.length);
assert.match(legacy.downloadUrl, /\/agc\/dev-win\/[\d.]+\//u);
});
});
test('next release version follows the higher local or channel version', () => {
assert.equal(compareVersions('0.1.15', '0.1.12'), 1); assert.equal(compareVersions('0.1.15', '0.1.12'), 1);
assert.equal(nextPatchVersion('0.1.12', '0.1.15'), '0.1.16'); assert.equal(nextPatchVersion('0.1.12', '0.1.15'), '0.1.16');
assert.equal(nextPatchVersion('0.1.18', '0.1.15'), '0.1.19'); assert.equal(nextPatchVersion('0.1.18', '0.1.15'), '0.1.19');
assert.equal(nextPatchVersion('0.1.12', null), '0.1.13'); assert.equal(nextPatchVersion('0.1.12', null), '0.1.13');
}); });
test('release upload forces overwrite for versioned artifact and latest pointer', () => { test('release upload forces overwrite for artifact, signature and channel pointers', () => {
const source = readFileSync( const source = readFileSync(
new URL('./release-upload.mjs', import.meta.url), new URL('./release-upload.mjs', import.meta.url),
'utf8', 'utf8',
); );
assert.equal( assert.equal(
(source.match(/runOssutil\(\['cp', '--force'/gu) ?? []).length, (source.match(/runOssutil\(\[\s*'cp',\s*'--force'/gu) ?? []).length,
2, 4,
); );
assert.match(source, /agc\/\$\{channel\}\/latest\.json/u);
assert.match(source, /agc\/latest\.json/u);
}); });
@@ -121,6 +121,10 @@ const allowedUncalledTauriCommands = [
'open_game_creator_launcher_window', 'open_game_creator_launcher_window',
'open_game_creator_workspace_window', 'open_game_creator_workspace_window',
'read_direct_project_conversation', 'read_direct_project_conversation',
// 项目定时快照上传只在 Rust 侧触发(周期定时器 / 工作区窗口关闭)与排障调用;
// 按产品口径不做客户端可见界面,因此同 `open_game_creator_*_window` 一样按 native-only 登记。
'read_local_project_snapshot_state',
'sync_local_project_snapshot',
'reset_design_agent_session', 'reset_design_agent_session',
'stop_local_game_preview_if_matches', 'stop_local_game_preview_if_matches',
'start_game_creator_external_mcp', 'start_game_creator_external_mcp',
@@ -724,6 +724,7 @@ function canvasAssetCall(agentId) {
assetKind: 'art-spritesheet', assetKind: 'art-spritesheet',
assetLabel: '游戏首版核心美术素材', assetLabel: '游戏首版核心美术素材',
replaceExisting: false, replaceExisting: false,
sliceMode: 'connected-components',
}); });
} }
@@ -2691,7 +2692,7 @@ function createDeterministicCanvasFixture(apiKey) {
'deterministic spritesheet fixture', 'deterministic spritesheet fixture',
model: 'deterministic-canvas-v1', model: 'deterministic-canvas-v1',
provider: 'deterministic-loopback', provider: 'deterministic-loopback',
sliceLayout: 'grid-2x2', sliceMode: 'connected-components',
spritesheetResource: { spritesheetResource: {
resourceId, resourceId,
projectId, projectId,
@@ -0,0 +1,20 @@
/**
* `agc` 开发启动下发给 Vite 的客户端特性开关默认值。
*
* 开发态默认关闭客户端更新检查:`npm run agc` / `agc:serve` 启动的客户端不请求 OSS
* 更新清单,也不显示更新入口。需要联调更新流程时显式传
* `VITE_AGC_ENABLE_APP_UPDATE_CHECK=1`;此处不覆盖已经显式配置的取值。
*/
const agcAppUpdateCheckEnvKey = 'VITE_AGC_ENABLE_APP_UPDATE_CHECK';
function withAgcDevFeatureFlags(env = process.env) {
if (String(env[agcAppUpdateCheckEnvKey] ?? '').trim()) {
return env;
}
return {
...env,
[agcAppUpdateCheckEnvKey]: '0',
};
}
export { agcAppUpdateCheckEnvKey, withAgcDevFeatureFlags };
@@ -0,0 +1,27 @@
/**
* 发布上传的 OSS 命令行整理:把 ossutil 参数与凭据整理成可执行或可打印的形式,
* 便于在 dry-run 下核对将要执行的上传,同时保证任何输出都不回显凭据明文。
*/
const redactedCredential = '<redacted>';
export function readReleaseDryRun(env = process.env) {
const value = env.AGC_RELEASE_DRY_RUN?.trim().toLowerCase();
return value === '1' || value === 'true';
}
function quoteArgument(value) {
return /[\s"']/u.test(value) ? JSON.stringify(value) : value;
}
export function formatOssutilCommand({ binary, args, endpoint, credentials }) {
const parts = [binary, ...args, '--endpoint', endpoint];
if (credentials) {
parts.push(
'--access-key-id',
redactedCredential,
'--access-key-secret',
redactedCredential,
);
}
return parts.map(quoteArgument).join(' ');
}
@@ -0,0 +1,44 @@
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { test } from 'node:test';
import { formatOssutilCommand, readReleaseDryRun } from './release-oss.mjs';
test('dry run only accepts explicit truthy values', () => {
assert.equal(readReleaseDryRun({}), false);
assert.equal(readReleaseDryRun({ AGC_RELEASE_DRY_RUN: '1' }), true);
assert.equal(readReleaseDryRun({ AGC_RELEASE_DRY_RUN: ' true ' }), true);
assert.equal(readReleaseDryRun({ AGC_RELEASE_DRY_RUN: '0' }), false);
assert.equal(readReleaseDryRun({ AGC_RELEASE_DRY_RUN: '' }), false);
});
test('printed upload command keeps arguments and hides credentials', () => {
const command = formatOssutilCommand({
binary: 'ossutil',
args: [
'cp',
'--force',
'陶泥儿 0.1.48.exe',
'oss://agc-dev/agc/dev-win/x.exe',
],
endpoint: 'oss-rg-china-mainland.aliyuncs.com',
credentials: true,
});
assert.match(command, /^ossutil cp --force /u);
assert.match(command, /"陶泥儿 0\.1\.48\.exe"/u);
assert.match(command, /oss:\/\/agc-dev\/agc\/dev-win\/x\.exe/u);
assert.match(
command,
/--access-key-id <redacted> --access-key-secret <redacted>/u,
);
});
test('uploader gates every ossutil call behind the dry run switch', () => {
const source = readFileSync(
new URL('./release-upload.mjs', import.meta.url),
'utf8',
);
assert.match(source, /const dryRun = readReleaseDryRun\(\);/u);
assert.match(source, /if \(dryRun\) \{/u);
assert.match(source, /dry-run:未写入任何 OSS 对象/u);
});
@@ -1,6 +1,8 @@
import { spawnSync } from 'node:child_process'; import { spawnSync } from 'node:child_process';
import path from 'node:path'; import path from 'node:path';
import { formatOssutilCommand, readReleaseDryRun } from './release-oss.mjs';
const bucket = process.env.AGC_OSS_BUCKET?.trim() || 'agc-dev'; const bucket = process.env.AGC_OSS_BUCKET?.trim() || 'agc-dev';
const endpoint = const endpoint =
process.env.AGC_OSS_ENDPOINT?.trim() || 'oss-rg-china-mainland.aliyuncs.com'; process.env.AGC_OSS_ENDPOINT?.trim() || 'oss-rg-china-mainland.aliyuncs.com';
@@ -8,6 +10,7 @@ if (!/^[a-z0-9][a-z0-9.-]{1,62}$/u.test(bucket) || /[\r\n\0]/u.test(endpoint)) {
throw new Error('OSS bucket 或 endpoint 配置无效'); throw new Error('OSS bucket 或 endpoint 配置无效');
} }
process.env.AGC_UPDATE_OSS_BASE_URL ||= `https://${bucket}.${endpoint}/agc`; process.env.AGC_UPDATE_OSS_BASE_URL ||= `https://${bucket}.${endpoint}/agc`;
const dryRun = readReleaseDryRun();
const { generateUpdateManifest, prepareReleaseVersion, runTauriBuild } = const { generateUpdateManifest, prepareReleaseVersion, runTauriBuild } =
await import('./build-release.mjs'); await import('./build-release.mjs');
@@ -19,6 +22,18 @@ function runOssutil(args) {
if (Boolean(accessKeyId) !== Boolean(accessKeySecret)) { if (Boolean(accessKeyId) !== Boolean(accessKeySecret)) {
throw new Error('OSS AccessKey ID 和 Secret 必须同时提供'); throw new Error('OSS AccessKey ID 和 Secret 必须同时提供');
} }
if (dryRun) {
// 演练:只打印将要执行的上传,凭据以占位符呈现,不写入 OSS。
console.log(
`[dry-run] ${formatOssutilCommand({
binary,
args,
endpoint,
credentials: Boolean(accessKeyId),
})}`,
);
return;
}
const credentialArgs = accessKeyId const credentialArgs = accessKeyId
? ['--access-key-id', accessKeyId, '--access-key-secret', accessKeySecret] ? ['--access-key-id', accessKeyId, '--access-key-secret', accessKeySecret]
: []; : [];
@@ -38,11 +53,40 @@ function runOssutil(args) {
await prepareReleaseVersion(); await prepareReleaseVersion();
runTauriBuild([]); runTauriBuild([]);
const { artifact, manifestPath, manifest } = generateUpdateManifest(); const { artifact, channel, legacyManifestPath, manifest, manifestPath } =
const artifactKey = `agc/${manifest.version}/${path.basename(artifact)}`; generateUpdateManifest();
const artifactKey = `agc/${channel}/${manifest.version}/${path.basename(artifact)}`;
// Jenkins/ossutil 默认会在目标对象已存在时交互询问并按默认值跳过; // Jenkins/ossutil 默认会在目标对象已存在时交互询问并按默认值跳过;
// 发布清单是固定的 latest 指针,必须显式覆盖,否则流水线会误报成功但远端仍保留旧版本。 // 发布清单是固定的 latest 指针,必须显式覆盖,否则流水线会误报成功但远端仍保留旧版本。
runOssutil(['cp', '--force', artifact, `oss://${bucket}/${artifactKey}`]); runOssutil(['cp', '--force', artifact, `oss://${bucket}/${artifactKey}`]);
runOssutil(['cp', '--force', manifestPath, `oss://${bucket}/agc/latest.json`]); runOssutil([
'cp',
'--force',
`${artifact}.sig`,
`oss://${bucket}/${artifactKey}.sig`,
]);
runOssutil([
'cp',
'--force',
manifestPath,
`oss://${bucket}/agc/${channel}/latest.json`,
]);
console.log(`[ai-game-creator-shell] 已上传 oss://${bucket}/${artifactKey}`); console.log(`[ai-game-creator-shell] 已上传 oss://${bucket}/${artifactKey}`);
console.log(`[ai-game-creator-shell] 已上传 oss://${bucket}/agc/latest.json`); console.log(
`[ai-game-creator-shell] 已上传 oss://${bucket}/agc/${channel}/latest.json`,
);
if (legacyManifestPath) {
// 迁移桥:让仍走旧 sha256 清单的已发布客户端升级到新协议,一个版本周期后删除。
runOssutil([
'cp',
'--force',
legacyManifestPath,
`oss://${bucket}/agc/latest.json`,
]);
console.log(
`[ai-game-creator-shell] 已上传迁移指针 oss://${bucket}/agc/latest.json`,
);
}
if (dryRun) {
console.log('[ai-game-creator-shell] dry-run:未写入任何 OSS 对象');
}
@@ -3,6 +3,7 @@ import http from 'node:http';
import net from 'node:net'; import net from 'node:net';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { withAgcDevFeatureFlags } from './dev-feature-flags.mjs';
import { resolveAgcDevEndpoint, withAgcDevEndpointEnv } from './dev-port.mjs'; import { resolveAgcDevEndpoint, withAgcDevEndpointEnv } from './dev-port.mjs';
const appRoot = fileURLToPath(new URL('..', import.meta.url)); const appRoot = fileURLToPath(new URL('..', import.meta.url));
@@ -104,7 +105,7 @@ const child = spawn(
], ],
{ {
cwd: appRoot, cwd: appRoot,
env: withAgcDevEndpointEnv(endpoint), env: withAgcDevFeatureFlags(withAgcDevEndpointEnv(endpoint)),
stdio: 'inherit', stdio: 'inherit',
// Node 18.20+/20+/24 on Windows rejects spawning .cmd (npm.cmd) without a shell (EINVAL). // Node 18.20+/20+/24 on Windows rejects spawning .cmd (npm.cmd) without a shell (EINVAL).
shell: true, shell: true,
@@ -11,6 +11,7 @@ import {
stopWindowsProcessTree, stopWindowsProcessTree,
stopWindowsWorktreeProcesses, stopWindowsWorktreeProcesses,
} from '../../../scripts/dev-windows-process.mjs'; } from '../../../scripts/dev-windows-process.mjs';
import { withAgcDevFeatureFlags } from './dev-feature-flags.mjs';
import { import {
agcVitePortEnvKey, agcVitePortEnvKey,
readAgcDevEndpoint, readAgcDevEndpoint,
@@ -978,7 +979,10 @@ async function startVite(apiTarget, endpoint = readAgcDevEndpoint()) {
'--port', '--port',
String(endpoint.port), String(endpoint.port),
], ],
{ cwd: appRoot, env: withAgcDevEndpointEnv(endpoint) }, {
cwd: appRoot,
env: withAgcDevFeatureFlags(withAgcDevEndpointEnv(endpoint)),
},
); );
} }
+240 -12
View File
@@ -814,6 +814,16 @@ dependencies = [
"url", "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]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.10.1" version = "0.10.1"
@@ -837,7 +847,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97"
dependencies = [ dependencies = [
"bitflags 2.13.0", "bitflags 2.13.0",
"core-foundation", "core-foundation 0.10.1",
"core-graphics-types", "core-graphics-types",
"foreign-types 0.5.0", "foreign-types 0.5.0",
"libc", "libc",
@@ -850,7 +860,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
dependencies = [ dependencies = [
"bitflags 2.13.0", "bitflags 2.13.0",
"core-foundation", "core-foundation 0.10.1",
"libc", "libc",
] ]
@@ -1405,6 +1415,16 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "filetime"
version = "0.2.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759"
dependencies = [
"cfg-if",
"libc",
]
[[package]] [[package]]
name = "find-msvc-tools" name = "find-msvc-tools"
version = "0.1.9" version = "0.1.9"
@@ -1725,7 +1745,7 @@ dependencies = [
[[package]] [[package]]
name = "genarrative-ai-game-creator-shell" name = "genarrative-ai-game-creator-shell"
version = "0.1.45" version = "0.1.47"
dependencies = [ dependencies = [
"agent-runtime-core", "agent-runtime-core",
"axum", "axum",
@@ -1747,7 +1767,6 @@ dependencies = [
"oxc_parser", "oxc_parser",
"oxc_semantic", "oxc_semantic",
"oxc_span", "oxc_span",
"percent-encoding",
"platform-agent", "platform-agent",
"platform-llm", "platform-llm",
"portable-pty", "portable-pty",
@@ -1766,6 +1785,7 @@ dependencies = [
"tauri-plugin-dialog", "tauri-plugin-dialog",
"tauri-plugin-http", "tauri-plugin-http",
"tauri-plugin-opener", "tauri-plugin-opener",
"tauri-plugin-updater",
"tempfile", "tempfile",
"tokio", "tokio",
"toml 0.8.2", "toml 0.8.2",
@@ -1776,7 +1796,7 @@ dependencies = [
"url", "url",
"uuid", "uuid",
"windows-sys 0.61.2", "windows-sys 0.61.2",
"zip", "zip 2.4.2",
] ]
[[package]] [[package]]
@@ -2220,9 +2240,11 @@ dependencies = [
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"socket2", "socket2",
"system-configuration",
"tokio", "tokio",
"tower-service", "tower-service",
"tracing", "tracing",
"windows-registry",
] ]
[[package]] [[package]]
@@ -2506,6 +2528,36 @@ dependencies = [
"windows-sys 0.45.0", "windows-sys 0.45.0",
] ]
[[package]]
name = "jni"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498"
dependencies = [
"cfg-if",
"combine",
"jni-macros",
"jni-sys 0.4.1",
"log",
"simd_cesu8",
"thiserror 2.0.18",
"walkdir",
"windows-link 0.2.1",
]
[[package]]
name = "jni-macros"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3"
dependencies = [
"proc-macro2",
"quote",
"rustc_version",
"simd_cesu8",
"syn 2.0.118",
]
[[package]] [[package]]
name = "jni-sys" name = "jni-sys"
version = "0.3.1" version = "0.3.1"
@@ -2819,6 +2871,12 @@ dependencies = [
"unicase", "unicase",
] ]
[[package]]
name = "minisign-verify"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22f9645cb765ea72b8111f36c522475d2daa0d22c957a9826437e97534bc4e9e"
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.8.9" version = "0.8.9"
@@ -3234,6 +3292,18 @@ dependencies = [
"objc2-core-foundation", "objc2-core-foundation",
] ]
[[package]]
name = "objc2-osa-kit"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f112d1746737b0da274ef79a23aac283376f335f4095a083a267a082f21db0c0"
dependencies = [
"bitflags 2.13.0",
"objc2",
"objc2-app-kit",
"objc2-foundation",
]
[[package]] [[package]]
name = "objc2-quartz-core" name = "objc2-quartz-core"
version = "0.3.2" version = "0.3.2"
@@ -3378,6 +3448,20 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "osakit"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b"
dependencies = [
"objc2",
"objc2-foundation",
"objc2-osa-kit",
"serde",
"serde_json",
"thiserror 2.0.18",
]
[[package]] [[package]]
name = "outref" name = "outref"
version = "0.5.2" version = "0.5.2"
@@ -4360,15 +4444,20 @@ dependencies = [
"http-body", "http-body",
"http-body-util", "http-body-util",
"hyper", "hyper",
"hyper-rustls",
"hyper-util", "hyper-util",
"js-sys", "js-sys",
"log", "log",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"rustls",
"rustls-pki-types",
"rustls-platform-verifier",
"serde", "serde",
"serde_json", "serde_json",
"sync_wrapper", "sync_wrapper",
"tokio", "tokio",
"tokio-rustls",
"tokio-util", "tokio-util",
"tower", "tower",
"tower-http", "tower-http",
@@ -4482,6 +4571,33 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "rustls-platform-verifier"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0"
dependencies = [
"core-foundation 0.10.1",
"core-foundation-sys",
"jni 0.22.4",
"log",
"once_cell",
"rustls",
"rustls-native-certs",
"rustls-platform-verifier-android",
"rustls-webpki",
"security-framework",
"security-framework-sys",
"webpki-root-certs",
"windows-sys 0.61.2",
]
[[package]]
name = "rustls-platform-verifier-android"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
[[package]] [[package]]
name = "rustls-webpki" name = "rustls-webpki"
version = "0.103.13" version = "0.103.13"
@@ -4609,7 +4725,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
dependencies = [ dependencies = [
"bitflags 2.13.0", "bitflags 2.13.0",
"core-foundation", "core-foundation 0.10.1",
"core-foundation-sys", "core-foundation-sys",
"libc", "libc",
"security-framework-sys", "security-framework-sys",
@@ -4952,6 +5068,22 @@ version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
[[package]]
name = "simd_cesu8"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520"
dependencies = [
"rustc_version",
"simdutf8",
]
[[package]]
name = "simdutf8"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
[[package]] [[package]]
name = "similar" name = "similar"
version = "2.7.0" version = "2.7.0"
@@ -5175,6 +5307,27 @@ dependencies = [
"syn 2.0.118", "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]] [[package]]
name = "system-deps" name = "system-deps"
version = "6.2.2" version = "6.2.2"
@@ -5196,7 +5349,7 @@ checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9"
dependencies = [ dependencies = [
"bitflags 2.13.0", "bitflags 2.13.0",
"block2", "block2",
"core-foundation", "core-foundation 0.10.1",
"core-graphics", "core-graphics",
"crossbeam-channel", "crossbeam-channel",
"dbus", "dbus",
@@ -5206,7 +5359,7 @@ dependencies = [
"gdkwayland-sys", "gdkwayland-sys",
"gdkx11-sys", "gdkx11-sys",
"gtk", "gtk",
"jni", "jni 0.21.1",
"libc", "libc",
"log", "log",
"ndk", "ndk",
@@ -5239,6 +5392,17 @@ dependencies = [
"syn 2.0.118", "syn 2.0.118",
] ]
[[package]]
name = "tar"
version = "0.4.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840"
dependencies = [
"filetime",
"libc",
"xattr",
]
[[package]] [[package]]
name = "target-lexicon" name = "target-lexicon"
version = "0.12.16" version = "0.12.16"
@@ -5262,7 +5426,7 @@ dependencies = [
"gtk", "gtk",
"heck 0.5.0", "heck 0.5.0",
"http", "http",
"jni", "jni 0.21.1",
"libc", "libc",
"log", "log",
"mime", "mime",
@@ -5477,6 +5641,39 @@ dependencies = [
"zbus", "zbus",
] ]
[[package]]
name = "tauri-plugin-updater"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b28d8cabdeb0564f03ae261963de4bc3d98321cd3d213e76a81b7d344e5df606"
dependencies = [
"base64 0.22.1",
"dirs",
"flate2",
"futures-util",
"http",
"infer",
"log",
"minisign-verify",
"osakit",
"percent-encoding",
"reqwest 0.13.4",
"rustls",
"semver",
"serde",
"serde_json",
"tar",
"tauri",
"tauri-plugin",
"tempfile",
"thiserror 2.0.18",
"time",
"tokio",
"url",
"windows-sys 0.60.2",
"zip 4.6.1",
]
[[package]] [[package]]
name = "tauri-runtime" name = "tauri-runtime"
version = "2.11.3" version = "2.11.3"
@@ -5487,7 +5684,7 @@ dependencies = [
"dpi", "dpi",
"gtk", "gtk",
"http", "http",
"jni", "jni 0.21.1",
"objc2", "objc2",
"objc2-ui-kit", "objc2-ui-kit",
"objc2-web-kit", "objc2-web-kit",
@@ -5510,7 +5707,7 @@ checksum = "fe41e015bf8fc4d6477ff4926a0ef769dc64ff34c7b0038b6f7cacae892acb5c"
dependencies = [ dependencies = [
"gtk", "gtk",
"http", "http",
"jni", "jni 0.21.1",
"log", "log",
"objc2", "objc2",
"objc2-app-kit", "objc2-app-kit",
@@ -6586,6 +6783,15 @@ dependencies = [
"system-deps", "system-deps",
] ]
[[package]]
name = "webpki-root-certs"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b"
dependencies = [
"rustls-pki-types",
]
[[package]] [[package]]
name = "webpki-roots" name = "webpki-roots"
version = "1.0.9" version = "1.0.9"
@@ -7192,7 +7398,7 @@ dependencies = [
"gtk", "gtk",
"http", "http",
"javascriptcore-rs", "javascriptcore-rs",
"jni", "jni 0.21.1",
"libc", "libc",
"ndk", "ndk",
"objc2", "objc2",
@@ -7256,6 +7462,16 @@ version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
[[package]]
name = "xattr"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156"
dependencies = [
"libc",
"rustix",
]
[[package]] [[package]]
name = "yoke" name = "yoke"
version = "0.8.3" version = "0.8.3"
@@ -7437,6 +7653,18 @@ dependencies = [
"zopfli", "zopfli",
] ]
[[package]]
name = "zip"
version = "4.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1"
dependencies = [
"arbitrary",
"crc32fast",
"indexmap 2.14.0",
"memchr",
]
[[package]] [[package]]
name = "zmij" name = "zmij"
version = "1.0.21" version = "1.0.21"
@@ -1,11 +1,14 @@
[package] [package]
name = "genarrative-ai-game-creator-shell" name = "genarrative-ai-game-creator-shell"
version = "0.1.45" version = "0.1.47"
edition = "2021" edition = "2021"
publish = false publish = false
[features] [features]
default = [] default = []
# 模板库假数据注入(仅本地页面压测/演示用):只有显式开启该 feature 才会编译并在读取清单后
# 把条目循环补齐成假数据;计数由 AGC_TEMPLATE_LIBRARY_SYNTHETIC_COUNT 控制(默认 1000)。
template-library-fixtures = []
cocos-editor = ["cocos-editor-bridge/process-discovery"] cocos-editor = ["cocos-editor-bridge/process-discovery"]
cocos-editor-execute = ["cocos-editor", "cocos-editor-bridge/windows-bootstrap"] cocos-editor-execute = ["cocos-editor", "cocos-editor-bridge/windows-bootstrap"]
cocos-editor-injection = ["cocos-editor-execute", "cocos-editor-bridge/windows-injection"] cocos-editor-injection = ["cocos-editor-execute", "cocos-editor-bridge/windows-injection"]
@@ -47,7 +50,6 @@ similar = "2.7"
platform-llm = { path = "../../../server-rs/crates/platform-llm" } platform-llm = { path = "../../../server-rs/crates/platform-llm" }
platform-agent = { path = "../../../server-rs/crates/platform-agent" } platform-agent = { path = "../../../server-rs/crates/platform-agent" }
portable-pty = "0.9" portable-pty = "0.9"
percent-encoding = "2"
reqwest = { version = "0.12", default-features = false, features = ["json", "multipart", "native-tls", "stream"] } reqwest = { version = "0.12", default-features = false, features = ["json", "multipart", "native-tls", "stream"] }
regex = "1" regex = "1"
shared-contracts = { path = "../../../server-rs/crates/shared-contracts", default-features = false } shared-contracts = { path = "../../../server-rs/crates/shared-contracts", default-features = false }
@@ -55,6 +57,7 @@ tauri = { version = "2.11.2", features = [] }
tauri-plugin-dialog = "2.7.1" tauri-plugin-dialog = "2.7.1"
tauri-plugin-http = { version = "2.5.9", default-features = false, features = ["charset", "cookies", "http2", "rustls-tls"] } tauri-plugin-http = { version = "2.5.9", default-features = false, features = ["charset", "cookies", "http2", "rustls-tls"] }
tauri-plugin-opener = "2" tauri-plugin-opener = "2"
tauri-plugin-updater = "2.11.0"
tempfile = "3" tempfile = "3"
toml = "0.8" toml = "0.8"
ttf-parser = "0.25.1" ttf-parser = "0.25.1"
@@ -14,13 +14,13 @@
"allow": [ "allow": [
{ "url": "https://dev.genarrative.world/api/*" }, { "url": "https://dev.genarrative.world/api/*" },
{ "url": "https://www.genarrative.world/api/*" }, { "url": "https://www.genarrative.world/api/*" },
{ "url": "https://agc-dev.oss-rg-china-mainland.aliyuncs.com/*" },
{ "url": "https://*/api/*" }, { "url": "https://*/api/*" },
{ "url": "http://localhost:*/*" }, { "url": "http://localhost:*/*" },
{ "url": "http://127.0.0.1:*/*" } { "url": "http://127.0.0.1:*/*" }
] ]
}, },
"opener:default", "opener:default",
"updater:default",
"dialog:allow-open", "dialog:allow-open",
"dialog:allow-save" "dialog:allow-save"
] ]
@@ -2,7 +2,7 @@
{"type":"function","function":{"name":"get_workflow_status","description":"读取当前策划工作流状态,只返回阶段列表、当前阶段、已批准阶段和待审批阶段;不推进阶段、不提交审批、不修改文件。","parameters":{"type":"object","properties":{},"additionalProperties":false}}}, {"type":"function","function":{"name":"get_workflow_status","description":"读取当前策划工作流状态,只返回阶段列表、当前阶段、已批准阶段和待审批阶段;不推进阶段、不提交审批、不修改文件。","parameters":{"type":"object","properties":{},"additionalProperties":false}}},
{"type":"function","function":{"name":"list_resources","description":"列出固定资源的逻辑目录、资源 ID、标题和简介。资源是只读的随包文档;不要猜测物理路径。","parameters":{"type":"object","properties":{},"additionalProperties":false}}}, {"type":"function","function":{"name":"list_resources","description":"列出固定资源的逻辑目录、资源 ID、标题和简介。资源是只读的随包文档;不要猜测物理路径。","parameters":{"type":"object","properties":{},"additionalProperties":false}}},
{"type":"function","function":{"name":"read_resource","description":"读取一份固定资源文档全文。每次读取一个 resource_id;资源只读。读到未实现占位文档时由你自行判断和处理。","parameters":{"type":"object","properties":{"resource_id":{"type":"string"}},"required":["resource_id"],"additionalProperties":false}}}, {"type":"function","function":{"name":"read_resource","description":"读取一份固定资源文档全文。每次读取一个 resource_id;资源只读。读到未实现占位文档时由你自行判断和处理。","parameters":{"type":"object","properties":{"resource_id":{"type":"string"}},"required":["resource_id"],"additionalProperties":false}}},
{"type":"function","function":{"name":"patch_file","description":"局部修改 UTF-8 文件。使用 old_text/new_text,或使用 edits 一次进行多个独立替换;每个 old_text 必须非空且在原文件中唯一,匹配失败、重复或范围重叠时不修改文件。path 使用相对路径。","parameters":{"type":"object","properties":{"path":{"type":"string"},"old_text":{"type":"string"},"new_text":{"type":"string"},"edits":{"type":"array","items":{"type":"object","properties":{"old_text":{"type":"string"},"new_text":{"type":"string"}},"required":["old_text","new_text"],"additionalProperties":false}}},"required":["path"],"additionalProperties":false}}}, {"type":"function","function":{"name":"patch_file","description":"局部修改 UTF-8 文件。使用 old_text/new_text,或使用 edits 一次进行多个独立替换;每个 old_text 必须非空且在原文件中唯一。所有 edit 会一次性校验;任何失败都不修改文件,错误会列出各失败项及可唯一匹配的其余项。path 使用相对路径。","parameters":{"type":"object","properties":{"path":{"type":"string"},"old_text":{"type":"string"},"new_text":{"type":"string"},"edits":{"type":"array","items":{"type":"object","properties":{"old_text":{"type":"string"},"new_text":{"type":"string"}},"required":["old_text","new_text"],"additionalProperties":false}}},"required":["path"],"additionalProperties":false}}},
{"type":"function","function":{"name":"delete_path","description":"谨慎使用;永久删除工作区内的文件或目录;目录会连同全部内容递归删除,不备份。先确认目标及删除范围。path 使用相对路径,不能删除工作区根目录,也不能经过链接。","parameters":{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false}}}, {"type":"function","function":{"name":"delete_path","description":"谨慎使用;永久删除工作区内的文件或目录;目录会连同全部内容递归删除,不备份。先确认目标及删除范围。path 使用相对路径,不能删除工作区根目录,也不能经过链接。","parameters":{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false}}},
{"type":"function","function":{"name":"list_dir","description":"列出工作目录内的文件和目录。path 使用相对路径。","parameters":{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false}}}, {"type":"function","function":{"name":"list_dir","description":"列出工作目录内的文件和目录。path 使用相对路径。","parameters":{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false}}},
{"type":"function","function":{"name":"read_file","description":"读取工作目录内的 UTF-8 文本文件。path 使用相对路径。","parameters":{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false}}}, {"type":"function","function":{"name":"read_file","description":"读取工作目录内的 UTF-8 文本文件。path 使用相对路径。","parameters":{"type":"object","properties":{"path":{"type":"string"}},"required":["path"],"additionalProperties":false}}},
@@ -13,8 +13,8 @@ Let the client derive projections from real disk changes and trusted tool result
2. Before using or deriving an existing registered asset, call `agc_list_registered_assets` and select its `localAssetId`. If the user points to an existing project file that is not listed, first call `agc_list_project_files`; only entries with `assetImportable=true` (recognized image, font, audio, video, document, or code files) may be passed to `agc_import_account_assets.localPaths`. Then re-read `agc_list_registered_assets`; never infer a source identity from a filename or fabricate a localAssetId. 2. Before using or deriving an existing registered asset, call `agc_list_registered_assets` and select its `localAssetId`. If the user points to an existing project file that is not listed, first call `agc_list_project_files`; only entries with `assetImportable=true` (recognized image, font, audio, video, document, or code files) may be passed to `agc_import_account_assets.localPaths`. Then re-read `agc_list_registered_assets`; never infer a source identity from a filename or fabricate a localAssetId.
3. Keep read scopes separate: `asset.list` is the current project manifest, `asset.library.list` is the signed-in account library, and the web project's canvas resource read model is the authoritative canvas list. The account library is not the complete canvas list. 3. Keep read scopes separate: `asset.list` is the current project manifest, `asset.library.list` is the signed-in account library, and the web project's canvas resource read model is the authoritative canvas list. The account library is not the complete canvas list.
4. Use `canvas.asset_import` for safe account/canvas asset IDs or project-relative local paths. The client rechecks ownership and validates bytes; host absolute paths require native UI file-picker authorization. 4. Use `canvas.asset_import` for safe account/canvas asset IDs or project-relative local paths. The client rechecks ownership and validates bytes; host absolute paths require native UI file-picker authorization.
5. When the user explicitly asks to create or derive video, character animation, sound effect, or background music, call `agc_create_or_derive_resource`. Use `create` only for video/audio without a source and `derive` with a registered `sourceLocalAssetId`; character animation is always derived from an image. 5. When the user explicitly asks to create or derive video, character animation, sound effect, or background music, call `agc_create_or_derive_resource`. Use `create` only for video/audio without a source and `derive` with a registered `sourceLocalAssetId`; character animation is always derived from an image. Keep `prompt` inside the per-kind limit that the client really enforces: background music at most 140 characters, sound effect at most 1900, video and character animation at most 4000. A longer prompt is rejected before submission, so write the short version first instead of retrying the same text.
6. When the user explicitly asks to remove an image background, call `agc_remove_background` with a registered image `sourceLocalAssetId` and an output name. The client requires the signed-in account, owns canvas/folder context and task identity, and returns only bounded queue state. 6. When the user explicitly asks to remove an image background, call `agc_remove_background` with a registered image `sourceLocalAssetId` and `assetName`. Optional `backgroundMode` is `complex` (semantic foreground segmentation; default) or `flat` (solid-colour background removal). Prefer `flat` when the background is known to be solid. Only `flat` accepts optional `screenColor`: `auto`, `#RRGGBB`, or omitted for automatic detection by the service. Do not select a colour on behalf of `auto`. The client requires the signed-in account, owns canvas/folder context and task identity, and returns only bounded queue state.
7. Preserve existing relative paths when a small edit is sufficient so client resource identities remain stable. 7. Preserve existing relative paths when a small edit is sufficient so client resource identities remain stable.
8. Do not edit `.agent/manifest.json`, revision counters, version records, resource IDs, canvas identities, source provenance, generation ledgers, or browser receipts by hand. 8. Do not edit `.agent/manifest.json`, revision counters, version records, resource IDs, canvas identities, source provenance, generation ledgers, or browser receipts by hand.
9. Do not create a version when no game file changed. The client compares content fingerprints and advances revision only after an actual source change. 9. Do not create a version when no game file changed. The client compares content fingerprints and advances revision only after an actual source change.
@@ -14,4 +14,6 @@ Read scopes remain separate: `asset.list` is the current project's local manifes
`agc_create_or_derive_resource` accepts only semantic intent. The client resolves `sourceLocalAssetId`, creates stable request identities, recovers matching pending operations, serializes paid submissions, writes supported media into the current canvas and same-name asset folder, validates downloaded bytes, commits the local manifest transaction, and returns redacted warnings. A tool error or timeout is not permission to generate again with a new identity. `agc_create_or_derive_resource` accepts only semantic intent. The client resolves `sourceLocalAssetId`, creates stable request identities, recovers matching pending operations, serializes paid submissions, writes supported media into the current canvas and same-name asset folder, validates downloaded bytes, commits the local manifest transaction, and returns redacted warnings. A tool error or timeout is not permission to generate again with a new identity.
`agc_remove_background` is the semantic image post-processing path. It accepts only a registered image `sourceLocalAssetId` and output name; the client resolves the formal source resource, canvas/folder context, stable operation identity, idempotency key, and authenticated External v1 `/api/external/v1/editor/images/background-removals` call. Its result is bounded queue state; Codex must not poll internal workers, construct source URLs, or retry with a new identity after an uncertain response. `prompt` limits are per kind and are enforced before any paid submission: background music accepts 1-140 characters, sound effect 1-1900, video and character animation 1-4000, and image editing (`agc_edit_image`) 1-32000. The client composes the submitted request from a fixed prefix plus your prompt, so an over-limit prompt fails locally with the exact limit; shorten the text rather than resubmitting the same value. `agc_edit_image` remains the image path; this tool never generates or edits still images.
`agc_remove_background` accepts a registered image `sourceLocalAssetId`, `assetName`, and optional `backgroundMode` and `screenColor`. `complex` uses semantic segmentation to identify the foreground; `flat` removes a solid-colour background. Prefer `flat` when the background is known to be solid; omitting the mode selects `complex`. Only `flat` accepts a colour: `auto`, `#RRGGBB`, or omitted for automatic service detection. Never infer a concrete colour for `auto`. Empty or invalid values and colour without `flat` are rejected. The client resolves the formal source resource, canvas/folder context, stable operation identity, idempotency key, and authenticated External v1 `/api/external/v1/editor/images/background-removals` call. Mode and colour are part of request identity. Its result is bounded queue state; Codex must not poll internal workers, construct source URLs, or retry with a new identity after an uncertain response.

Some files were not shown because too many files have changed in this diff Show More