Compare commits

..

114 Commits

Author SHA1 Message Date
kdletters 405b36e8d5 补充UI组件复用与共享库沉淀规范
Project CI / Repository checks (pull_request) Failing after 12s
Project CI / Backend tests (pull_request) Failing after 12s
Project CI / Native shell tests (pull_request) Successful in 14m37s
Project CI / Frontend tests (pull_request) Successful in 16m26s
在 AGENTS.md 增加 UI 组件优先复用与共享库沉淀原则

同步更新团队协作约定中的 UI 组件边界
2026-08-31 13:20:32 +08:00
kdletters 7e7ef50e40 继续扩展共享组件并收口入口页面
新增 PlatformBackActionButton 共享返回动作与行为测试

迁移登录、绑定手机号和世界实体目录的共享组件直引

扩展示展示页示例并同步组件库文档与决策记录
2026-08-31 11:53:04 +08:00
kdletters aadb09a9ea 继续收口共享组件并迁移业务页面
补充 PlatformToggleRow 共享整行开关组件与测试

将多个结果页、工作台和账号页面改用共享 Platform chrome

更新展示页、组件库文档与共享决策记录
2026-08-31 11:13:32 +08:00
kdletters 5d14af1324 补充共享整行开关组件
新增 PlatformToggleRow 共享实现与类型导出

保留 common 兼容出口并补充组件行为测试
2026-08-31 11:01:39 +08:00
kdletters 328da5e6eb 扩展共享组件库并接入当前项目
Project CI / Frontend tests (pull_request) Successful in 3m10s
Project CI / Native shell tests (pull_request) Successful in 12m4s
Project CI / Repository checks (pull_request) Failing after 12s
Project CI / Backend tests (pull_request) Failing after 12s
- 收口平台异步、筛选、图标、信息、列表、运行态和统计组件到共享包
- 新增 Label、Checkbox、Skeleton、Table 语义基础组件
- 展示页与代表性业务页面改用共享 canonical 导出并补齐交互反馈
- 同步共享样式、组件测试和前端架构决策文档
2026-08-28 18:26:10 +08:00
kdletters bbd895d7c2 补齐共享 UI 组件库与展示页交互
Project CI / Frontend tests (pull_request) Successful in 3m21s
Project CI / Repository checks (pull_request) Failing after 11s
Project CI / Backend tests (pull_request) Failing after 9s
Project CI / Native shell tests (pull_request) Successful in 12m36s
- 新增 shadcn canonical UI 源码、共享样式与组件导出
- 新增 /components 共享组件展示页及路由测试
- 补齐平台组件筛选、排序、上传预览和异步状态交互
- 同步网站与客户端构建别名、依赖和项目文档
2026-08-27 15:12:50 +08:00
kdletters d41aaddd0e 修复灵感图测试数量耦合
Project CI / Repository checks (push) Successful in 4m3s
Project CI / Frontend tests (push) Successful in 4m13s
Project CI / Backend tests (push) Successful in 5m8s
Project CI / Native shell tests (push) Successful in 13m48s
改为校验内置灵感图片非空,避免测试绑定具体资源数量。
2026-08-26 20:30:14 +08:00
kdletters 1ba0eaf59c Merge remote-tracking branch 'origin/master' into codex/push-master-20260826
Project CI / Frontend tests (push) Failing after 3m33s
Project CI / Repository checks (push) Failing after 3m42s
Project CI / Native shell tests (push) Failing after 4m39s
Project CI / Backend tests (push) Successful in 5m30s
2026-08-26 20:20:27 +08:00
kdletters b15cd2a5d6 开放客户端图片创作能力
新增普通图片、角色图、视觉规范图、UI 设计图和发布宣传图生成工具

新增已登记图片精修工具并复用客户端授权、幂等、下载和资源登记链路

修正图片生成账本对普通图路由与新类型的恢复识别

同步 AGC Skill、DirectProject 提示词和技术方案说明
2026-08-26 20:03:13 +08:00
kdletters c1f32d230f 修复客户端资源画布图片预览
限制美术资源总览初次适配倍率,避免位图插值放大
扩大美术资源聚焦态原图预览区域
补充布局模型测试与技术方案验收口径
2026-08-26 19:08:33 +08:00
kdletters d585ad93d5 修复 AGC 直连工具授权路由
普通客户端沿用当前 AGC 登录会话和账号路由

ExternalDeveloper 模式保留客户端内部私有凭据隔离

补齐系统提示词、工具描述、Skill 与技术方案的 API Key 边界

更新 Skill 清单版本和内容指纹
2026-08-26 18:33:01 +08:00
kdletters 1768989d38 修复开发态 AGC 工具进程启动路径
避免 Cargo 热重载后 current_exe 路径指向已删除旧二进制
Linux 开发态通过当前客户端进程的稳定 executable 引用启动 agc_tools
补充 DirectProject MCP 可执行路径回归测试
2026-08-26 17:42:43 +08:00
kdletters 1460f9be08 恢复 AGC 受控 Skill 与联网工具注入
恢复 DirectProject 隔离 Skill Pack 注入与启动校验
恢复 agc_tools STDIO MCP 及浏览器、美术、资源工具桥接
恢复按配置启用的 agc_web_search 与安全结果过滤
同步更新美术与试玩 Skill 的 MCP 使用契约
2026-08-26 17:33:31 +08:00
wuxiangwanzi aad9d064fc 新增首页灵感推荐瀑布流 (#196)
Project CI / Native shell tests (push) Failing after 3m55s
Project CI / Backend tests (push) Successful in 4m55s
Project CI / Repository checks (push) Failing after 2m38s
Project CI / Frontend tests (push) Failing after 3m7s
新增 23 张内置灵感图片素材

实现响应式瀑布流和纵向滚动

支持图片全屏预览与遮罩关闭

补充首页交互测试和技术文档

---------

Co-authored-by: 段舒康 <kdletters@qq.com>
Co-authored-by: kdletters <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/196
Co-authored-by: 五香丸子 <15518898337@163.com>
Co-committed-by: 五香丸子 <15518898337@163.com>
2026-08-26 16:56:03 +08:00
kdletters a8d4aae390 新增 AGC 设置关于页并展示客户端版本
Project CI / Frontend tests (push) Successful in 6m41s
Project CI / Repository checks (push) Successful in 6m43s
Project CI / Backend tests (push) Successful in 7m44s
Project CI / Native shell tests (push) Successful in 15m49s
增加独立关于页并使用品牌 Logo

从客户端 package.json 自动读取版本号

补充设置页回归测试、样式与技术方案
2026-08-26 16:22:30 +08:00
kdletters c7442663f2 修复原生壳CI的Cargo依赖锁定
Project CI / Repository checks (push) Successful in 4m12s
Project CI / Native shell tests (push) Successful in 14m45s
Project CI / Frontend tests (push) Successful in 5m6s
Project CI / Backend tests (push) Successful in 6m6s
Native shell job 预取 server-rs、桌面壳和 AGC 壳依赖

AGC 的 platform-llm、shared-contracts 与壳测试统一使用 --locked

补充 workflow 回归断言及 CI 排障文档
2026-08-26 15:53:51 +08:00
suzmii 2407295363 修复资源管理滚轮翻页偶发失效 (#197)
Project CI / Repository checks (push) Successful in 2m58s
Project CI / Frontend tests (push) Successful in 3m23s
Project CI / Backend tests (push) Successful in 5m0s
Project CI / Native shell tests (push) Successful in 13m37s
## 变更背景

资源管理画布在连续滚轮翻页时,可能出现偶发失效或中间栏目被逐页渲染,导致后续滚轮输入被资源渲染阻塞;同时,缩放、手动切页、排序和拖拽过程中还可能执行已经过期的翻页任务。

本 PR 将翻页意图与资源渲染提交解耦,并补齐交互取消边界,让连续输入始终基于最新逻辑目标栏目计算。

## 主要修改

- 滚轮分页改为同步维护逻辑目标栏目,可见栏目通过 React transition 异步提交;渲染尚未完成时,新滚轮事件仍基于最新逻辑目标继续计算,不逐页加载中间栏目。
- 待执行翻页队列改为有界合并与延迟 flush,修复旧定时器清理不完整的问题。
- Ctrl/Meta 缩放、触控板缩放手势、手动点击切页、切换排序、开始画布空白拖拽或资源卡拖拽时,取消尚未执行的过期翻页任务。
- viewport 更新显式携带目标栏目,画布拖拽记录发起时的栏目,避免过渡渲染期间把旧栏目 viewport 写入新栏目。
- 每个排序模式和栏目继续保留独立 viewport,复位、首次 fit 与保留用户当前平移缩放的路径统一使用同一套数据流。
- 依赖浮层的 ResizeObserver、scroll 和 resize 监听只在挂载时建立一次;viewport / 几何 revision 变化时通过 ref 合并调度一帧测量,避免连续输入时重复重建观察器。
- 更新资源自由画板技术方案,并补充回归测试覆盖连续滚轮、缩放取消、大步跳转、切页边界和依赖浮层观察器生命周期。

## 验证

- `npm run test -- apps/ai-game-creator-shell/tests/ResourceDependencyOverlay.test.ts apps/ai-game-creator-shell/tests/appSurface.test.ts --run`
  - 2 个测试文件通过
  - 393 / 393 tests passed
- `npm run agc:typecheck`
- `npm run lint:eslint -- apps/ai-game-creator-shell/src/view/project-development/index.tsx apps/ai-game-creator-shell/src/view/project-development/ResourceDependencyOverlay.tsx apps/ai-game-creator-shell/tests/ResourceDependencyOverlay.test.ts apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts`
- `npm run check:encoding`
- `git diff --check`

以上均为合并 `origin/master@2f3c578db` 后在 `b5c98af42` 上执行。

Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/197
Co-authored-by: suzmii <suzmii@qq.com>
Co-committed-by: suzmii <suzmii@qq.com>
2026-08-26 15:29:35 +08:00
suzmii 92425dfea2 修复 AGC 运行时测试回归与 Provider 错误分类问题 (#198)
Project CI / Repository checks (push) Successful in 4m7s
Project CI / Frontend tests (push) Successful in 5m57s
Project CI / Backend tests (push) Successful in 6m55s
Project CI / Native shell tests (push) Successful in 16m41s
## 背景

本 PR 修复了最新 master 基线上发现的 AGC 测试失败,包括 Direct Runtime Skill 契约、敏感信息脱敏、外部生成 5xx 结果未知、Provider transport 错误分类,以及本地 mock 请求体读取问题。主要集中在:

- Direct Runtime 提示词和 Skill 索引契约未同步;
- 浏览器诊断、委派回执和 Runtime 失败投影的敏感信息脱敏回归;
- macOS 临时目录路径被误判为不安全链接祖先;
- 流式和画布 mock 服务只读取请求头,导致请求体断言失败;
- 本机 HTTP 代理将 loopback 连接关闭改写为 HTTP 502,导致 transport 错误分类错误;
- 平台图片生成收到 5xx 时,无法正确区分确定拒绝和结果未知。

本 PR 不包含资源管理滚轮分页相关前端改动。

## 修复内容

### Direct Runtime 与 Skill 契约

- 补齐审核 Skill 索引及 manifest 指纹。
- 恢复 Direct Runtime 提示词相关测试。
- 保持系统提示词长度、审核索引范围和敏感信息边界不变。

### 敏感信息投影

- 浏览器诊断在进入同线程修复前统一脱敏。
- 委派回执严格隔离敏感上下文。
- Runtime 失败投影不再泄漏命令行密码参数或旧计划明细。

### 外部生成与 Provider 错误分类

- 平台图片生成提交收到 5xx 时统一返回“结果未知”。
- 保留本地恢复账本,禁止将 5xx 当作确定拒绝。
- `platform-llm` 对 `localhost`、IPv4/IPv6 loopback 地址禁用环境代理,避免代理伪造 502,恢复真实 transport/stream 错误分类。

### 测试与 macOS 边界

- 流式 LLM、External Canvas mock 服务改为读取完整 HTTP 请求体。
- 允许 macOS `/var` 到 `/private/var` 的系统临时目录祖先链接。
- 继续拒绝用户项目中的链接祖先。
- macOS 下跳过不满足平台前提的大小写敏感路径和非 UTF-8 文件名测试。

## 影响范围

- AGC Direct Runtime
- Provider Runtime
- External Canvas 生成恢复
- `platform-llm` loopback HTTP 客户端
- 相关 Rust 测试基础设施

不改变公网 API、SpacetimeDB schema、资源管理滚轮分页行为或外部生产 Provider 的代理策略。

## 验证结果

- AGC Rust 完整测试:`2303 passed, 0 failed, 14 ignored`
- `platform-llm` 测试:`132 passed, 0 failed`
- `npm run check:encoding`:通过
- `git diff --check`:通过

## 分支与提交

- 分支:`codex/fix-agc-runtime-test-regressions`
- 基线:`origin/master`
- 最新提交:`94c5443a2 修复AGC运行时测试与本地请求回归`

Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/198
Co-authored-by: suzmii <suzmii@qq.com>
Co-committed-by: suzmii <suzmii@qq.com>
2026-08-26 15:29:28 +08:00
kdletters 2f3c578db4 发布 AGC 标准版 0.1.8
Project CI / Repository checks (push) Successful in 3m46s
Project CI / Frontend tests (push) Successful in 11m26s
Project CI / Native shell tests (push) Failing after 6m54s
Project CI / Backend tests (push) Successful in 17m1s
同步 AI 游戏创作器的 package、Cargo、Tauri 与锁文件版本。

更新版本一致性脚本及其测试夹具。
2026-08-26 10:43:29 +08:00
kdletters 5c416ce5b5 Merge remote-tracking branch 'origin/master' into codex/docs-current-master
Project CI / Repository checks (push) Successful in 12m13s
Project CI / Frontend tests (push) Successful in 13m20s
Project CI / Backend tests (push) Successful in 23m7s
Project CI / Native shell tests (push) Failing after 31m49s
2026-08-26 10:12:00 +08:00
kdletters 969dcc0272 整理项目文档并统一当前口径
- 删除过时 PRD、设计、计划和退役文档
- 收口文档索引、产品基线、玩法链路和共享记忆
- 清理历史决策重复内容、断链和本机路径
- 更新 AGC Runtime、Provider、后端与运维当前约束
2026-08-26 10:11:46 +08:00
kdletters 2ac97b3f5e 退役客户端 MCP 约束与运行链路
Project CI / Repository checks (push) Successful in 3m50s
Project CI / Frontend tests (push) Successful in 4m41s
Project CI / Backend tests (push) Successful in 5m35s
Project CI / Native shell tests (push) Failing after 9m59s
删除客户端 MCP 配置、Runtime 工具和安全门禁

移除 MCP E2E 套件、测试夹具和测试辅助代码

清理 MCP 依赖及 Skill/Prompt 中的 MCP 约束
2026-08-26 02:32:05 +08:00
kdletters a8c7cd2f2b 修复导入素材后资源画布不刷新
支持账户素材和项目内 PNG/JPEG/WEBP 本地素材导入后的 manifest 失效通知

增加资源画布导入图片回归测试并扩展素材发现上下文

同步更新 Agent 导入契约说明
2026-08-26 02:32:04 +08:00
kdletters 6b947c5430 修复 AGC 项目素材发现与受控导入
发现项目内未登记媒体并保持正式素材身份边界

新增 Runtime 与 Direct MCP 素材查询和导入工具

支持图片素材在 AssetImporter 中受控登记

补充路径安全、契约测试和技术文档
2026-08-26 02:32:04 +08:00
kdletters 990b71965c 修复陶泥儿错误诊断脱敏与资源参数提示
分离普通 Prompt 与错误诊断脱敏,保留安全 HTTP 字段并隐藏敏感值
补充 assetName 必填说明与 schema 回归测试
增加前端错误标记过滤及 HTTP 诊断回归测试
同步更新 AI 游戏创作智能体实施方案
2026-08-26 02:32:04 +08:00
lhk229 05e11b11ca 恢复被 #193 误删的策划前端全部样式,并加上组件到样式的守门测试 (#195)
Project CI / Repository checks (push) Successful in 4m6s
Project CI / Frontend tests (push) Successful in 4m26s
Project CI / Backend tests (push) Successful in 5m33s
Project CI / Native shell tests (push) Successful in 13m50s
症状:立项策划界面整体裸奔——审批卡、阶段进度条、交付行没有任何框和布局,
「查看 GDD 正文」弹层因 backdrop 丢掉 fixed 定位而内联平铺进消息流。

成因:策划前端经 squash PR #159 完整进入 master,组件(TSX)至今与源分支最终态
逐字节一致;随后 #193「Codex/agent chat layout fix」重写聊天区样式时,把
styles.css 里策划前端的选择器整段删除——gdd-approval-card 34 条、plan-gdd* 17
条、planning-lane-runtime-strip 上限——未搬往任何其他文件(全仓检索为 0)。同一
个 PR 把 project-development.suite.ts 大幅重写,原有的 CSS 守门断言一并消失,
所以没有任何测试变红。

修复:从 5802048c3^ 原样取回全部 42 个规则块,整段追加在 styles.css 末尾并注明
来历。这些选择器在当前文件中出现次数为 0,纯加法、零冲突,不触碰 #193/#194 有意
重构的聊天区规则;追加在末尾也保持了原有的同权重覆盖关系(策划窄条的上限仍压过
调试面板的 240px 天花板)。

守门:plan-gdd.suite.ts(两轮样式重构都未被触碰的文件)新增一条测试,类名清单
直接从 GddApprovalCard / PlanningLaneRuntimeStrip 源码推导,逐个断言 styles.css
里有对应规则,另单独锁弹层 backdrop 的 fixed 定位。A/B 验证:抽掉恢复段即红,报
「styles.css 缺少 .plan-gdd-surface 的规则」。组件加新类而样式缺失、或样式再次
被顺手清理,这条都会拦住。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/195
Co-authored-by: Linghong <ink29535@proton.me>
Co-committed-by: Linghong <ink29535@proton.me>
2026-08-25 18:45:17 +08:00
kdletters 7cb79e3aa4 发布 AGC 标准版 0.1.7
Project CI / Repository checks (push) Successful in 3m59s
Project CI / Frontend tests (push) Successful in 4m42s
Project CI / Backend tests (push) Successful in 5m9s
Project CI / Native shell tests (push) Successful in 13m42s
同步 AI 游戏创作器的 package、Cargo、Tauri 与锁文件版本。

更新版本一致性脚本及其测试夹具。
2026-08-25 16:08:54 +08:00
kdletters c09a511aaa Codex/fix pr188 191 ci (#194)
Project CI / Repository checks (push) Successful in 2m24s
Project CI / Frontend tests (push) Successful in 2m54s
Project CI / Backend tests (push) Successful in 4m33s
Project CI / Native shell tests (push) Successful in 14m29s
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/194
Co-authored-by: kdletters <kdletters@qq.com>
Co-committed-by: kdletters <kdletters@qq.com>
2026-08-25 15:41:55 +08:00
kdletters a265a0ff92 AGC 标准版升级至 0.1.6
Project CI / Repository checks (push) Failing after 4m14s
Project CI / Frontend tests (push) Failing after 5m8s
Project CI / Backend tests (push) Failing after 5m11s
Project CI / Native shell tests (push) Failing after 5m25s
统一 AI 游戏创作器的 package、Cargo 与 Tauri 版本号。

隔离 Developer Key 与 GUI 平台会话,并约束 DirectProject 原生 patch 路径。

补充对应回归测试与项目决策记录。
2026-08-25 15:18:01 +08:00
lhk229 31ebe4c78c 修复残留问题 (#192)
Project CI / Repository checks (push) Failing after 3m37s
Project CI / Frontend tests (push) Failing after 5m42s
Project CI / Backend tests (push) Failing after 5m40s
Project CI / Native shell tests (push) Failing after 5m27s
agent.db 是纯 append、全仓没有 rotation,而有界尾窗的记录数上限(16384)比写侧
的扫描上限(100 万)低两个数量级。截断掐掉的永远是更旧的记录,所以「命中」与截
断无关;只有「没命中」时才真的分不清「没写过」和「没看见」。

- plan_gdd_decision_audit_state_locked:截断判断从遍历前的无条件 Err 挪到遍历后、
  且只在没命中时报。原先任何写够记录的项目都会永久停在 needs-reconciliation——
  audit 明明就在窗口里,函数照样报错,文案还指向「identity 不一致」。
- approval_terminal_observation_exists_locked:不再把截断位丢给 `_`。没命中且截
  断时返回 Err 而不是 Ok(false),交给调用方既有的具名 projection gap 分支,不再
  让一次视野缺失冒充恢复缺口被反复重放。
- 收束闸 Err 分支标题改为「Fast GDD decision audit 无法确认」。Err 现在有 identity
  冲突和尾窗截断两种来源,具体原因由 detail 里的 error 原文给出。
- AGENT_DB_MAX_BOUNDED_RECORDS 改 pub(crate),让回归测试能精确把 fixture 撑过截
  断线,不必在测试里埋魔数。

新增 truncated_tail_scan_still_confirms_records_inside_the_window 钉住这两处:把
任一处改回旧行为都会让它失败。

recovery_scan.rs 的七槽审计是同样的形状,但它断言的是精确基数而非存在性,截断确
实证不了基数,另行处理。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: 段舒康 <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/192
Co-authored-by: 孔令弘 <ink29535@proton.me>
Co-committed-by: 孔令弘 <ink29535@proton.me>
2026-08-25 14:27:31 +08:00
kdletters 96c2e4be44 发布AGC标准版0.1.5
Project CI / Frontend tests (push) Failing after 4m48s
Project CI / Repository checks (push) Failing after 5m3s
Project CI / Native shell tests (push) Failing after 5m48s
Project CI / Backend tests (push) Failing after 5m50s
同步Node、Tauri与Cargo版本

更新workspace与配置版本门禁
2026-08-25 14:26:36 +08:00
kdletters c6a9c00439 同步远端主线并发布 UI 工作流修复
Project CI / Repository checks (push) Failing after 4m16s
Project CI / Backend tests (push) Failing after 5m3s
Project CI / Frontend tests (push) Failing after 5m4s
Project CI / Native shell tests (push) Failing after 5m48s
- 合并远端客户端布局与版本修复
- 保留多模态 UI 工作流与鉴权错误分类
2026-08-25 13:57:01 +08:00
kdletters 96c5444972 修复 Codex app-server 鉴权错误分类
- 识别 401/403 上游鉴权失败并归类为 unauthorized

- 增加错误分类回归测试并保持敏感诊断脱敏

- 更新 UI 工作流 Runtime 鉴权失败门禁说明
2026-08-25 13:47:35 +08:00
kdletters 10786c9672 打通多模态 UI 工作流与 Agent Runtime 路由
- 接入 Codex app-server 原生图片输入与隔离暂存

- 统一 UI 识别、合并、绑定到 Agent Runtime Provider

- 增加页面自动发现、UI 资源桥接与阶段持久化

- 同步画布编辑器投影、测试与技术文档
2026-08-25 13:06:46 +08:00
wuxiangwanzi 5802048c30 Codex/agent chat layout fix (#193)
Project CI / Repository checks (push) Failing after 4m12s
Project CI / Frontend tests (push) Failing after 4m18s
Project CI / Native shell tests (push) Failing after 5m9s
Project CI / Backend tests (push) Successful in 5m45s
Co-authored-by: kdletters <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/193
Co-authored-by: 五香丸子 <15518898337@163.com>
Co-committed-by: 五香丸子 <15518898337@163.com>
2026-08-25 12:36:51 +08:00
kdletters 23ea6d4906 升级AGC标准发行版patch版本
Project CI / Repository checks (push) Failing after 4m43s
Project CI / Frontend tests (push) Failing after 3m36s
Project CI / Backend tests (push) Successful in 8m28s
Project CI / Native shell tests (push) Failing after 6m43s
将AI游戏创作壳版本升级到0.1.4

同步Tauri、Cargo及版本校验门禁
2026-08-25 10:49:15 +08:00
wuxiangwanzi 07afb6d753 增加资源页签新资源红点 (#191)
Project CI / Native shell tests (push) Failing after 5m3s
Project CI / Backend tests (push) Successful in 6m1s
Project CI / Repository checks (push) Failing after 3m53s
Project CI / Frontend tests (push) Failing after 3m20s
按项目会话追踪非当前栏目新增资源并在切换后清除
为页签名称添加红点样式和无障碍标识
补充资源页签行为测试及工作台文档

---------

Co-authored-by: 段舒康 <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/191
Co-authored-by: 五香丸子 <15518898337@163.com>
Co-committed-by: 五香丸子 <15518898337@163.com>
2026-08-25 10:48:12 +08:00
wuxiangwanzi 77b090fc77 隐藏首页模糊项目提示 (#188)
Project CI / Frontend tests (push) Failing after 3m2s
Project CI / Backend tests (push) Successful in 4m40s
Project CI / Repository checks (push) Failing after 2m37s
Project CI / Native shell tests (push) Failing after 4m13s
首页默认项目状态改为空
移除最近项目标题下的解释性副标题
补充首页视觉规则并更新界面测试

---------

Co-authored-by: 段舒康 <kdletters@qq.com>
Co-authored-by: kdletters <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/188
Co-authored-by: 五香丸子 <15518898337@163.com>
Co-committed-by: 五香丸子 <15518898337@163.com>
2026-08-25 10:47:20 +08:00
kdletters 9f8a24fc23 解锁AGC直连Codex原生工具
Project CI / Backend tests (push) Successful in 4m6s
Project CI / Repository checks (push) Successful in 3m31s
Project CI / Frontend tests (push) Successful in 4m5s
Project CI / Native shell tests (push) Successful in 14m2s
精简DirectProject系统提示词与浏览器证据回传
开放受沙箱约束的原生文件搜索命令和Skill
通过本地凭据代理隔离Provider真实密钥
保留agc_tools账本与Runtime持久化权威
同步更新AGC技能包指纹和架构文档
2026-08-24 22:21:23 +08:00
kdletters 4b35a0cabd Merge pull request '恢复资源排列胶囊分段按钮' (#190) from codex/restore-resource-sort-segmented-control into master
Project CI / Repository checks (push) Successful in 3m24s
Project CI / Frontend tests (push) Successful in 3m35s
Project CI / Native shell tests (push) Successful in 17m42s
Project CI / Backend tests (push) Successful in 4m58s
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/190
2026-08-24 21:56:53 +08:00
wuxiangwanzi 84f993260a 恢复资源排列胶囊分段按钮
Project CI / Repository checks (pull_request) Successful in 3m0s
Project CI / Frontend tests (pull_request) Successful in 3m39s
Project CI / Backend tests (pull_request) Successful in 4m51s
Project CI / Native shell tests (pull_request) Successful in 19m37s
将依赖与类型恢复为同一胶囊内的左右分段按钮。

保留按依赖与按类型的无障碍名称并恢复可见短文案。

新增真实 DOM 结构回归,防止后续合并再次拆散按钮组。
2026-08-24 21:51:25 +08:00
lhk229 3d8e0211db 5分钟策划agent (#159)
Project CI / Repository checks (push) Successful in 3m54s
Project CI / Frontend tests (push) Successful in 4m20s
Project CI / Backend tests (push) Successful in 4m33s
Project CI / Native shell tests (push) Successful in 16m31s
Co-authored-by: 段舒康 <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/159
Co-authored-by: Linghong <ink29535@proton.me>
Co-committed-by: Linghong <ink29535@proton.me>
2026-08-24 21:44:44 +08:00
suzmii 532c6d51fd 完善 Game Agent 资源画布与图片精修生成事务 (#181)
Project CI / Repository checks (push) Successful in 4m5s
Project CI / Frontend tests (push) Successful in 4m48s
Project CI / Backend tests (push) Successful in 6m6s
Project CI / Native shell tests (push) Successful in 15m24s
## 背景

本合并请求整合 Game Agent 资源管理的栏目分页自由画布,以及图片持续精修、候选生成和本地恢复事务。

当前远端比较基线:

- base:`master` @ `b00a3f80576949567bf3320f0c6c92b4ed0a649d`
- head:`codex/game-agent-resource-section-pages` @ `5795d2f9b8797c342dcf2920ccefec2a3ff3f472`
- 相对 base:28 commits、33 files(`+3746 / -1333`)

## 主要改动

### 资源管理分页自由画布

- 固定展示“设计文档 → 美术资源 → 音乐音效 → 游戏代码 → 项目版本”五个栏目;空栏目仍可从悬浮 Dock 打开空画布。
- `按依赖` 与 `按类型` 共用栏目分页画布;每个“排序模式 + 栏目”组合独立保存 viewport。
- 普通滚轮使用有界意图队列切换栏目;Ctrl/Meta + 滚轮以指针为锚点缩放;空白拖拽可沿 x/y 无限平移,不以资源 extent 作为导航边界。
- 资源卡支持拖拽布局与一次 CAS 持久化;切页、排序切换和卸载会统一取消拖拽及 pointer capture。
- 资源详情为非模态独立卡片:打开、切换或关闭详情不卸载背景画布、资源卡或依赖连线,也不重置 viewport、搜索或排序状态。
- 顶部已移除“生成视频 / 生成音效 / 生成背景音乐 / 新增 UI 设计”的手动入口;保留播放、未完成编辑恢复、排序和画布复位,以及资源详情中的既有编辑动作。

### 图片持续精修与生成事务

- 图片资源可进入唯一活动精修草稿,支持原生批量导入、图片下方快速编辑卡、候选图层、任务侧栏和“设为最终图”。
- 生成成功只合并候选图层和 generation 权威事实,不以全量 hydrate 覆盖生成期间的本地编辑。
- 候选媒体、图层和公开 generation 记录写入并回读成功后,才推进私有 `candidate-ready`。
- 候选确认接入 Surface 保存 FIFO 与重新打开 hydrate;revision-sensitive 操作等待确认屏障。
- 失败归档和正式图提交采用可恢复中间态,避免中断后出现幽灵任务、重复 revision 或不一致的正式资产。

### 合同与文档

- `acknowledgeCandidateLayers`、`importLocalImages`、`archiveFailedGeneration` 为必选 Host Port;不支持的宿主返回结构化 `unsupported-capability`。
- 同步更新 TypeScript / Rust 合同、Tauri command 可达性、PRD、技术方案与项目共享记忆。
- 明确资源栏目无限画布合同:普通平移不夹取;资源 extent、图片测量、布局变更和 resize 不得重置用户 viewport;仅首次可测量布局与显式复位按真实卡片包围盒适配内容。

## 验证

CI run [#1290](https://git.genarrative.world/git/GenarrativeAI/Genarrative/actions/runs/1290) 针对当前 head 已完成且四项均成功:

- [x] Repository checks
- [x] Frontend tests(235 test files、3166 tests passed;App Surface 410 tests)
- [x] Backend tests
- [x] Native shell tests

另有本地定向检查:

- [x] `npm run agc:typecheck`
- [x] 资源画布、App Surface、Asset Canvas 与资源实时集成定向回归
- [x] Rust 候选确认、候选中断恢复、失败归档与恢复定向测试
- [x] `cargo fmt --check`
- [x] `npm run check:encoding`
- [x] `git diff --check`

## 评审 provenance

- review #247、#248、#252 都针对旧 head,当前在 Gitea 中均标记为 stale;不将其写作“已处理 #247”或当前 head 的评审结论。
- review #253 针对旧 `5456a1d…` head,提出 PRD 退役入口、共享 pitfalls 的 extent 旧合同和 PR 正文事实更新三项 P2;对应文档已在 `5795d2f…` head 修复并推送。
- 早期由 PR #176 引入的候选持久化、归档、Host Port 与快速编辑卡问题,当前已在 base/head 中具备对应修复,不作为本轮重复阻断项。
- 当前 head 仍需新的、针对最新提交的审批;历史 stale review 不能替代当前 head approval。

## 影响范围

- 修改 AI 游戏创作 App 的资源管理、图片精修、Tauri 本地持久化与恢复语义,以及共享合同和权威文档。
- 会调整本地资源布局、精修 draft 和私有 generation ledger 的状态机。
- 不修改 SpacetimeDB schema,不新增或变更 `/api/external/v1` 路由。

---------

Co-authored-by: kdletters <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/181
Co-authored-by: suzmii <suzmii@qq.com>
Co-committed-by: suzmii <suzmii@qq.com>
2026-08-24 20:42:58 +08:00
kdletters b00a3f8057 移除资源画布顶部手动生成入口
Project CI / Repository checks (push) Successful in 6m9s
Project CI / Frontend tests (push) Successful in 6m19s
Project CI / Backend tests (push) Successful in 7m23s
Project CI / Native shell tests (push) Successful in 15m50s
删除资源画布顶部的生成视频、生成音效、生成背景音乐和新增 UI 设计按钮
移除仅服务于这些入口的前端 handler 与入口配置
保留 Agent 语义生成、已有资源编辑、生成动画和未完成任务恢复能力
补充顶部入口消失与画布级操作保留的定向回归测试
同步更新 AGC 配置白名单、技术方案与项目决策记录
2026-08-24 18:29:43 +08:00
suzmii 15a524e6e0 Feat: 完善 Game Agent 资源画板与图片精修链路 (#176)
Project CI / Native shell tests (push) Successful in 13m19s
Project CI / Repository checks (push) Successful in 3m31s
Project CI / Frontend tests (push) Successful in 4m16s
Project CI / Backend tests (push) Successful in 5m7s
## 摘要

本 PR 将 Game Agent 资源管理页升级为真实自由画板,并打通图片资源的快速编辑、候选生成、导入、失败归档与正式图提交流程。

同时补齐图片精修的事务恢复、候选身份校验、生成中任务恢复、鉴权重试,以及“设为最终图”后游戏运行资源的稳定刷新链路。

## 主要变更

### 1. 资源管理页自由画板

- “按依赖”视图改为统一自由画板:
  - 支持指针拖拽平移
  - 滚轮平移
  - `Ctrl / Meta + wheel` 以指针为锚点缩放
  - 支持复位和全量资源适配
- 增加隐藏导航边界:
  - 边界由全量资源世界范围加安全留白决定
  - 搜索过滤不会缩小边界
  - 用户不能把全部资源拖出可视区域
- 图片资源卡按真实宽高比展示:
  - 预览读取真实 `pixelWidth / pixelHeight`
  - 布局碰撞、世界范围、依赖连线共同消费同一资源矩形
  - 非图片资源保持固定卡片尺寸
- 点击资源改为非模态详情卡:
  - 背景画板不卸载
  - 顶部工具栏、搜索、排序、缩放状态不重置
  - 图片详情不再进入全屏页

### 2. 图片资源快速编辑与精修画布

- 点击图片资源后直接显示快速编辑卡。
- 图片精修顶栏收敛为紧凑操作:
  - 返回
  - 导入
  - 定位当前最终图
  - 撤销 / 重做
- 删除、修改、设为最终图进入选中图片的上下文卡片。
- 快速编辑生成不再阻塞整张画布:
  - 立即创建生成占位卡
  - 占位卡可拖动并持久化位置
  - 右上任务列表显示排队 / 生成中 / 完成 / 失败状态
  - 任务列表可折叠
- 生成失败只影响当前任务:
  - 保留失败占位
  - 可显式归档失败任务
  - `reconciliation-required` 任务不允许删除
- 原生多选图片导入:
  - 使用 Tauri 原生文件选择
  - 后端批量安全读取
  - 一次推进草稿 revision
  - 失败不留下半导入状态

### 3. 候选图与正式图事务

- 生成成功只加入草稿私有候选层,不直接修改 manifest。
- “设为最终图”是唯一正式资源切换入口。
- refine 保持原 asset ID 和来源身份,不追加新的业务资产身份。
- 正式文件使用不可变路径:

  ```text
  assets/canvas/<name>--<commitId>.png

---------

Co-authored-by: kdletters <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/176
Co-authored-by: suzmii <suzmii@qq.com>
Co-committed-by: suzmii <suzmii@qq.com>
2026-08-24 16:27:33 +08:00
kdletters e6105cdcf9 提升AGC标准安装包patch版本
Project CI / Repository checks (push) Successful in 4m31s
Project CI / Frontend tests (push) Successful in 5m9s
Project CI / Backend tests (push) Successful in 6m8s
Project CI / Native shell tests (push) Successful in 14m34s
同步标准客户端版本到0.1.2

更新workspace与配置版本门禁
2026-08-24 15:34:57 +08:00
kdletters c59b8d4a1e 接入 External v1 抠图工具
Project CI / Frontend tests (push) Successful in 4m34s
Project CI / Repository checks (push) Successful in 4m38s
Project CI / Backend tests (push) Successful in 5m24s
Project CI / Native shell tests (push) Successful in 14m14s
将 agc_remove_background 切换到 External v1 异步契约

补齐稳定幂等身份、画布目录上下文与有界队列投影

同步 Skill、技术方案与项目决策记录
2026-08-24 15:16:02 +08:00
kdletters 03add97d30 Merge remote-tracking branch 'origin/master' 2026-08-24 15:04:12 +08:00
kdletters 275cab876c 开放受控抠图语义工具
新增 agc_remove_background 工具目录与客户端桥接

绑定登录账号、画布目录、稳定任务身份与有界队列结果

同步 Skill、技术方案和项目决策记录

补充工具参数、队列投影与内容指纹测试
2026-08-24 15:03:37 +08:00
wuxiangwanzi 1334648246 优化客户端登录界面视觉 (#185)
Project CI / Repository checks (push) Successful in 2m28s
Project CI / Frontend tests (push) Successful in 3m28s
Project CI / Backend tests (push) Successful in 4m41s
Project CI / Native shell tests (push) Successful in 14m6s
登录检查、登录页和错误页统一使用陶泥儿产品形象与平台浅色主题。

优化登录卡片、输入控件、切换按钮及窄屏布局。

补充认证界面测试并同步更新客户端技术方案。

---------

Co-authored-by: 段舒康 <kdletters@qq.com>
Co-authored-by: kdletters <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/185
Co-authored-by: 五香丸子 <15518898337@163.com>
Co-committed-by: 五香丸子 <15518898337@163.com>
2026-08-24 14:38:15 +08:00
suzmii aa8e3507d1 新增 External v1 去背景生成链路 (#184)
Project CI / Repository checks (push) Successful in 4m20s
Project CI / Frontend tests (push) Successful in 4m31s
Project CI / Backend tests (push) Successful in 5m45s
Project CI / Native shell tests (push) Successful in 14m53s
新增外部去背景 API、MCP 工具与异步队列契约

补齐来源归属、媒体类型、幂等重放和画布原子持久化校验

修复 provenance 重建、assetKindOverride 门禁与 revision retry 竞态

同步 Python helper、Skill、OpenAPI 及项目文档

---------

Co-authored-by: kdletters <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/184
Co-authored-by: suzmii <suzmii@foxmail.com>
Co-committed-by: suzmii <suzmii@foxmail.com>
2026-08-24 14:37:32 +08:00
kdletters 042eb05b0e 提升AGC标准安装包patch版本
Project CI / Repository checks (push) Successful in 3m24s
Project CI / Frontend tests (push) Successful in 4m5s
Project CI / Backend tests (push) Successful in 5m4s
Project CI / Native shell tests (push) Successful in 13m0s
同步Tauri、Cargo与Node版本到0.1.1

更新AGC配置检查门禁
2026-08-24 12:42:56 +08:00
kdletters 299b52d71b Merge remote-tracking branch 'origin/master'
Project CI / Repository checks (push) Successful in 4m6s
Project CI / Frontend tests (push) Successful in 4m51s
Project CI / Backend tests (push) Successful in 6m15s
Project CI / Native shell tests (push) Successful in 15m50s
# Conflicts:
#	apps/ai-game-creator-shell/src-tauri/src/project/resource_editor.rs
2026-08-24 12:21:29 +08:00
kdletters 46053832fd 补齐 Codex 语义媒体资源能力
新增登记资源查询与媒体创建/派生工具

接入画布上下文、幂等账本与 warning 回放

更新客户端 Skill、技术方案与决策记录

补充资源生成与传输失败回归测试
2026-08-24 12:05:27 +08:00
kdletters 2449e77461 修复切换账号后画布资源不可编辑 (#182)
Project CI / Frontend tests (push) Successful in 5m10s
Project CI / Backend tests (push) Successful in 5m37s
Project CI / Repository checks (push) Successful in 4m24s
Project CI / Native shell tests (push) Successful in 13m9s
## 变更

- 新增按服务 origin、平台 userId / Developer Key 摘要和本地 projectId 分区的 External Editor 项目绑定
- 新增按当前 principal、远端项目、本地 assetId、源 SHA-256、媒体类型和 canonical kind 分区的资源绑定
- manifest 中的 canvasProjectId / resourceId / assetObjectId 仅保留来源信息,不再作为当前账号的可编辑授权
- 切换账号后从本地正式资源重新上传、confirm、登记;图片、视频、角色动画、素材画布参考、art-spec 派生和 Direct 恢复统一使用当前账号绑定
- prepared / accepted / running 账本继续冻结原 principal;账号变化或远端结果不确定时停止补偿并保留现场等待对账
- 同步技术方案、decision log 和 pitfalls

## Review 结论

- 两路独立代码 review 均未发现剩余 P0-P2
- Review 发现并关闭了 max-pass 测试误放宽问题,恢复为绑定最大轮次的强断言
- 首轮 CI 暴露两处本 PR import 排序错误,已在独立提交 09486f142 中修复并复核

## 验证

- Rust 完整测试:2301 passed,0 failed,16 ignored
- Rust 集成与构建测试:5 + 2 + 14 passed
- repository-ci 本地同构门禁通过:lint、typecheck、139 表 SpacetimeDB schema guard、403 个 appSurface 测试、web/admin-web build
- External Editor procedure 真实 smoke 通过:精确重放、冲突、删除 fail-close、并发和孤儿检查
- Encoding check:5594 files
- git diff --check 通过
- Gitea Project CI run 1253:Repository checks、Frontend、Backend、Native shell tests 全部通过
- 当前 head a0b8415be 已合并 origin/master 44ee28c43,PR 无冲突

## 后续依赖

PR #176 暴露了这一公共账号身份缺陷。该 PR 合并后,#176 需要 rebase,并删除或接入其局部 canonical cache,不能保留第二套账号绑定系统。

Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/182
Co-authored-by: kdletters <kdletters@qq.com>
Co-committed-by: kdletters <kdletters@qq.com>
2026-08-24 11:33:37 +08:00
wuxiangwanzi 44ee28c43f 让运行视窗按内容自适应 (#180)
Project CI / Repository checks (push) Successful in 3m38s
Project CI / Frontend tests (push) Successful in 3m55s
Project CI / Backend tests (push) Successful in 4m7s
Project CI / Native shell tests (push) Failing after 9m31s
为本地预览注入只读尺寸桥并校验 iframe 消息来源

按可用区域等比缩放完整游戏画面并移除横纵滚动条

补充前端与 Rust 回归测试并同步运行视窗文档

---------

Co-authored-by: kdletters <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/180
Co-authored-by: 五香丸子 <15518898337@163.com>
Co-committed-by: 五香丸子 <15518898337@163.com>
2026-08-24 10:54:06 +08:00
kdletters 0b546c9e4c 完善AGC受控客户端能力
Project CI / Repository checks (push) Successful in 4m44s
Project CI / Frontend tests (push) Successful in 6m6s
Project CI / Backend tests (push) Successful in 6m8s
Project CI / Native shell tests (push) Failing after 12m41s
补齐美术复用与显式重生成模式、稳定回合身份和幂等补偿恢复
收紧付费授权、四切片资源身份及告警投影边界
限制Direct Codex仅写真实game目录并接入受控联网搜索
持久化Direct用户与助手消息并修复同进程恢复竞争
同步审核Skill、技术文档、项目记忆和回归测试
2026-08-23 22:34:16 +08:00
wuxiangwanzi e3b229ac78 调整 Agent 对话栏 Logo 与工作台布局 (#183)
Project CI / Repository checks (push) Successful in 5m34s
Project CI / Frontend tests (push) Successful in 6m5s
Project CI / Backend tests (push) Successful in 12m14s
Project CI / Native shell tests (push) Successful in 22m32s
放大 Agent 栏产品 Logo 并接入正式钱包入口
移除工作台外层卡片圆角与间距,改用区域底色和竖向分隔
将资源依赖/类型切换整理为联通分段按钮

---------

Co-authored-by: kdletters <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/183
Co-authored-by: 五香丸子 <15518898337@163.com>
Co-committed-by: 五香丸子 <15518898337@163.com>
2026-08-23 22:09:47 +08:00
Git Hooks Test 8fe439feba 修复AGC资源生成的序列帧持久化与画布完成上下文
Project CI / Repository checks (push) Successful in 4m20s
Project CI / Frontend tests (push) Successful in 4m35s
Project CI / Backend tests (push) Successful in 6m59s
Project CI / Native shell tests (push) Successful in 17m52s
- 本地manifest资产新增imageSequenceFrames和imageSequenceDurationMs,角色动画完成后随账本恢复并写回manifest

- 新建视频、音效和背景音乐时创建或复用同名画板项目与素材库目录,并携带projectId、assetFolderId和canvasCompletion

- 资源编辑请求指纹纳入generationMode,旧账本继续通过legacy指纹兼容

- 同步更新共享契约、前端资源投影、实施计划文档与定向测试
2026-08-23 21:37:27 +08:00
Git Hooks Test e0319e80f0 补齐AGC资源生成能力并接入视频动画与音频生成
- 资源编辑命令新增create生成模式,支持无源视频、音效和背景音乐生成

- 图片聚焦态新增角色动画生成入口,提交角色动画接口并下载预览视频登记为本地媒体

- 资源页新增生成视频、生成音效、生成背景音乐入口

- 统一资源编辑面板按生成模式区分新建媒体资源与编辑现有资源

- 补充资源生成与角色动画定向测试,并更新AI游戏创作App实施计划文档
2026-08-23 21:37:27 +08:00
wuxiangwanzi b1ee6d816d 优化Agent对话用户消息气泡 (#179)
Project CI / Repository checks (push) Successful in 3m42s
Project CI / Frontend tests (push) Successful in 4m19s
Project CI / Backend tests (push) Successful in 5m16s
Project CI / Native shell tests (push) Successful in 15m27s
将用户消息改为右侧气泡显示。

统一项目对话、Agent聊天页和Agent对话弹窗样式。

补充消息宽度、换行、圆角和移动端友好布局。

---------

Co-authored-by: kdletters <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/179
Co-authored-by: 五香丸子 <15518898337@163.com>
Co-committed-by: 五香丸子 <15518898337@163.com>
2026-08-23 19:06:05 +08:00
wuxiangwanzi 814fc343e2 优化客户端主界面表现 (#177)
Project CI / Repository checks (push) Successful in 4m11s
Project CI / Frontend tests (push) Successful in 5m52s
Project CI / Backend tests (push) Successful in 5m59s
Project CI / Native shell tests (push) Successful in 12m34s
调整首页品牌区、创作类型按钮和输入框视觉比例。

重做最近项目横向信息卡并展示类型、更新时间和进度。

补充本地项目修改时间只读字段及首页回归测试。

同步更新 GameAgent 客户端实施文档。

---------

Co-authored-by: kdletters <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/177
Co-authored-by: 五香丸子 <15518898337@163.com>
Co-committed-by: 五香丸子 <15518898337@163.com>
2026-08-23 16:50:14 +08:00
Git Hooks Test c98bf4a17a 新增AGC受控联网搜索工具
Project CI / Native shell tests (push) Failing after 2m9s
Project CI / Repository checks (push) Successful in 4m23s
Project CI / Frontend tests (push) Successful in 4m34s
Project CI / Backend tests (push) Successful in 5m59s
在DirectProject的agc_tools MCP中按webSearchEnabled开关新增agc_web_search工具。

通过客户端loopback工具桥固定访问Bing RSS并返回有界公开HTTPS结果。

保持Codex原生webSearch禁用,并同步搜索开关到系统提示词与连接池隔离边界。

补充MCP目录、参数校验、结果过滤、真实搜索链路和配置回归测试,并更新技术文档。
2026-08-23 16:04:25 +08:00
kdletters 1a909704aa 预览镜像同时内置本地环境配置
Project CI / Repository checks (push) Successful in 2m52s
Project CI / Native shell tests (push) Failing after 2m50s
Project CI / Frontend tests (push) Successful in 4m11s
Project CI / Backend tests (push) Successful in 4m44s
预览 API 与 worker 通过 BuildKit secret 内置 .env.local

补齐预览控制面固定 .env.local 校验与文档

更新 Docker 构建边界测试
2026-08-23 14:29:46 +08:00
kdletters 78ebe063f3 修复Jenkins Web构建npm版本漂移
Project CI / Native shell tests (push) Failing after 2m18s
Project CI / Repository checks (push) Successful in 4m7s
Project CI / Frontend tests (push) Successful in 4m35s
Project CI / Backend tests (push) Successful in 5m30s
新增 Jenkins 用户级 npm 10.9.7 版本隔离引导
在 Web Build 各独立 shell 复用固定 npm 后执行 workspace 安装与门禁
收紧生产运维检查并同步 npm workspaces 文档与共享决策
2026-08-23 04:11:18 +08:00
kdletters 5c58539052 修复AGC配置脚本导入顺序
Project CI / Native shell tests (push) Failing after 2m49s
Project CI / Repository checks (push) Successful in 3m57s
Project CI / Frontend tests (push) Successful in 4m50s
Project CI / Backend tests (push) Successful in 5m31s
按仓库 ESLint 规则整理 TypeScript 导入分组,解除 master 推送门禁
2026-08-23 01:59:21 +08:00
kdletters d8a7e814f2 实现AGC直连增量进度反馈
新增 direct turn update 事件与安全活动、正文增量、序号及终态生命周期
接入 Codex app-server 活动观察并限制高频活动通知
在 AGC 项目聊天渲染过程卡并保护项目与回合隔离、乱序门禁和自动跟随
补齐 direct 增量反馈的前端、Rust 回归测试与 Direct Interaction Event v1 文档
2026-08-23 01:54:28 +08:00
kdletters 01b10aafb2 修复素材画布生成恢复与模态交互
素材生成凭据失败保留原幂等身份并支持登录后恢复
素材生成在远端提交、下载暂存和正式提交前复验会话与草稿状态
稳定 staging token 支持半提交修复并对冲突和文件系统异常失败关闭
素材画布三类模态框增加焦点圈闭、背景隔离和焦点恢复
Tauri 命令可达性改为 TypeScript AST 扫描并收紧 allowlist
补充生成恢复、取消态、staging 与模态交互回归及项目决策记录
2026-08-23 01:54:28 +08:00
Git Hooks Test 32da013545 修复AGC Skill跨平台指纹漂移
Project CI / Native shell tests (push) Failing after 2m5s
Project CI / Repository checks (push) Successful in 3m34s
Project CI / Frontend tests (push) Successful in 4m14s
Project CI / Backend tests (push) Successful in 5m58s
统一以UTF-8 LF规范化内容计算Skill与清单指纹
安装隔离Skill时写入规范化文本避免Windows混合换行
增加LF与CRLF等价及安装结果回归测试
同步Skill Pack版本与实施文档、技术方案和踩坑记录
2026-08-23 01:20:28 +08:00
JenkenB b7a6f853d5 修复 AGC 内置 Skill 内容指纹 (#175)
Project CI / Repository checks (push) Successful in 3m50s
Project CI / Native shell tests (push) Failing after 1m36s
Project CI / Frontend tests (push) Successful in 3m55s
Project CI / Backend tests (push) Successful in 4m30s
## 变更

- 重算并修正三项内置 AGC Skill 的内容指纹
- 将审核 Skill Pack 版本提升至 2026-08-22.2
- 补充指纹回归防范记录

## 验证

- AGC Skill Pack Rust 单测 4/4 通过
- npm run check:encoding 通过
- git diff --check 通过

---------

Co-authored-by: 段舒康 <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/175
Co-authored-by: 高物 <253518756@qq.com>
Co-committed-by: 高物 <253518756@qq.com>
2026-08-22 16:53:36 +08:00
kdletters 8cc1432b86 修复完整容器数据库内存上限
Project CI / Repository checks (push) Successful in 3m52s
Project CI / Frontend tests (push) Successful in 4m0s
Project CI / Backend tests (push) Successful in 5m26s
Project CI / Native shell tests (push) Failing after 1m56s
将完整容器 SpacetimeDB 内存上限提升到 2 GiB

增加基础 Compose 内存门禁

同步容器运维文档与项目长期记忆
2026-08-22 16:22:55 +08:00
kdletters d631253c34 将预览密钥内置到API镜像
Project CI / Repository checks (push) Successful in 4m32s
Project CI / Frontend tests (push) Successful in 4m34s
Project CI / Backend tests (push) Successful in 5m18s
Project CI / Native shell tests (push) Failing after 1m37s
固定Jenkins预览密钥宿主路径并收紧文件校验

通过BuildKit secret向API和工作进程镜像安装密钥文件

增加摘要缓存门禁和普通无密钥构建兼容验证

同步预览部署文档、安全边界与排障记忆
2026-08-22 15:45:40 +08:00
kdletters 27de28a487 迁移 npm workspaces 统一依赖边界
根目录统一管理九个 workspace 与唯一 lockfile

迁移 CI、Jenkins 和容器的根目录单次 npm ci

补齐 hoist 兼容、版本校验与 workspace 门禁

同步依赖边界方案、运维文档和长期记忆
2026-08-22 13:17:26 +08:00
Git Hooks Test 00b78c9701 治理Rust构建缓存膨胀
Project CI / Repository checks (push) Successful in 3m40s
Project CI / Backend tests (push) Successful in 4m9s
Project CI / Frontend tests (push) Successful in 4m10s
Project CI / Native shell tests (push) Failing after 11m55s
关闭测试 profile 的增量编译并对齐 AGC 开发调试配置

新增受控缓存审计和显式 incremental 清理命令

补齐安全测试、开发运维说明与团队踩坑记录
2026-08-22 11:52:14 +08:00
Git Hooks Test d83551dd65 恢复AGC首页三类创作入口
Project CI / Repository checks (push) Successful in 3m28s
Project CI / Backend tests (push) Successful in 4m3s
Project CI / Frontend tests (push) Successful in 4m6s
Project CI / Native shell tests (push) Failing after 12m2s
恢复做游戏、做素材、做方案的类型状态与分类提示

将受限 creationType 作为结构化首轮上下文传入项目内 Codex,保持用户原文不变

修正三项内置 Skill 指纹并补齐前端、Rust 与文档回归
2026-08-22 10:46:33 +08:00
Git Hooks Test 2d99a12196 修正首页自动建项目后进入项目对话
Project CI / Frontend tests (push) Failing after 3m35s
Project CI / Repository checks (push) Successful in 3m52s
Project CI / Backend tests (push) Successful in 4m22s
Project CI / Native shell tests (push) Successful in 15m1s
恢复首页自动项目创建并移除无项目对话入口

首条正文与附件在新项目工作台内交给直连 Codex

补充 Tauri 命令、配置门禁、AppSurface 测试与迁移计划
2026-08-21 17:48:01 +08:00
kdletters e8d9367df7 修复AGC依赖锁文件缺失 (#174)
Project CI / Repository checks (push) Successful in 3m30s
Project CI / Backend tests (push) Successful in 4m14s
Project CI / Frontend tests (push) Successful in 4m16s
Project CI / Native shell tests (push) Successful in 15m42s
## 问题

合并后的 `master` 连续暴露了三个既有的 AGC 原生构建 blocker:

1. `npm ci --prefix apps/ai-game-creator-shell` 因 lockfile 缺少 `@emnapi/core` / `@emnapi/runtime` 嵌套节点而失败。
2. 修复安装后,Linux Tauri build 又因通用配置无条件引用未生成的 Windows Codex 侧车而失败。
3. Native shell 全量测试继续暴露三项内置 Skill 指纹从初次提交起即与最终文件字节不一致,以及 Linux 会把 Windows 路径语法误判为普通相对路径。

这些问题都在画布优化合并前的 AGC 集成提交中已经存在;画布优化未修改 AGC manifest、lockfile、Tauri 配置或 Skill Pack。

## 修复

- 使用子包 manifest 重新生成独立 package-lock 元数据
- 恢复 Tailwind oxide WASM bundled `@emnapi` 节点并保留 npm 元数据规范化
- 把 Codex Windows x64 侧车映射从通用配置迁到 `tauri.windows.conf.json`
- 配置门禁同时约束通用配置无 Windows resource、Windows 配置保留完整白名单
- 重算三项内置 Skill 内容指纹并提升审核包版本
- Skill 引用路径按平台无关规则拒绝反斜杠、盘符、UNC、绝对路径和父目录段
- 补充 MCP 回归,并同步技术方案、迁移计划和长期排障记录

## 验证

- npm 10.9.7 / npm 9.2.0:`npm ci --prefix apps/ai-game-creator-shell` 通过
- Linux:`npm run ai-game-creator-shell:build -- --no-bundle` 通过
- Skill 指纹 5/5 复算匹配;Skill / MCP 定向测试通过
- `npm run check:native-shells` 完整通过:AGC Rust `2159 passed / 0 failed / 15 ignored`,最终输出 `[check:native-shells] OK`
- `npm run check:encoding`、`npm run check:rustfmt`、本次目标文件 Prettier、`git diff --check` 通过
- AGC、desktop、server-rs Cargo.lock 与两份 package-lock 均无漂移

Reviewed-on: #174
Co-authored-by: kdletters <kdletters@qq.com>
Co-committed-by: kdletters <kdletters@qq.com>
2026-08-21 17:38:58 +08:00
kdletters c39d63b65f 优化图片画布拖动性能 (#173)
Project CI / Frontend tests (push) Failing after 45s
Project CI / Native shell tests (push) Failing after 46s
Project CI / Repository checks (push) Failing after 47s
Project CI / Backend tests (push) Successful in 3m25s
## 修改

- 按动画帧合并高频拖动更新,结束时持久化最终状态
- 对未移动图层子树做 memo,保留并发资源与业务字段更新
- 补充拖动合帧、最终 flush、并发状态和图层渲染回归
- 同步真实位图性能验收与排障文档

## 验证

- 8 个相关测试文件 202/202 通过
- TypeScript typecheck、编码检查、Prettier、git diff --check 通过
- 12000×12000 画布、360 张独立 512×512 PNG:有效位置更新 23.44/s → 58.18/s,gap p95 119.8ms → 32.9ms,>50ms 停顿 32 → 0,持久化快照 240 → 1
- 可信 CDP 输入最终运动观测约 46.9 FPS,不以空转 rAF 代替拖动帧率

Reviewed-on: #173
Co-authored-by: kdletters <kdletters@qq.com>
Co-committed-by: kdletters <kdletters@qq.com>
2026-08-21 15:33:02 +08:00
Git Hooks Test f25fb2a768 合并远端 master 更新
Project CI / Frontend tests (push) Failing after 47s
Project CI / Repository checks (push) Failing after 48s
Project CI / Native shell tests (push) Failing after 53s
Project CI / Backend tests (push) Successful in 3m32s
保留远端 UI 编辑器与门禁清理改动

合入本地 AGC 直连 Codex 长任务修复

# Conflicts:
#	apps/ai-game-creator-shell/package-lock.json
#	apps/ai-game-creator-shell/src-tauri/Cargo.lock
#	apps/ai-game-creator-shell/src/view/project-development/index.tsx
#	apps/ai-game-creator-shell/tests/appSurface/home.suite.ts
2026-08-21 15:07:29 +08:00
Git Hooks Test 9d73f9a157 修复直连 Codex 长任务验收等待
扩大 DirectProject 基础、MCP 与整回合有界等待并统一工具超时

明确禁用 shell 时使用文件快照和结构化试玩证据,避免误报能力阻断

同步真实陶泥儿生图、幂等复用与双视口浏览器验收记录
2026-08-21 13:30:06 +08:00
kdletters 46baebbc10 清理过期测试与门禁
Project CI / Repository checks (push) Successful in 3m8s
Project CI / Frontend tests (push) Successful in 3m26s
Project CI / Backend tests (push) Successful in 3m58s
Project CI / Native shell tests (push) Successful in 15m16s
删除退役内容校验、视觉小说墓碑脚本和旧玩法门禁文档

移除死别名与退役分支触发并同步当前文档入口

为Gitea和Jenkins补齐可信SpacetimeDB schema比较基线

补充workflow、schema和生产运维防回归测试
2026-08-21 12:20:43 +08:00
menghao e4e3cab559 修复画布参考图上传删除链路 (#164)
Project CI / Repository checks (push) Successful in 3m3s
Project CI / Frontend tests (push) Successful in 3m32s
Project CI / Backend tests (push) Successful in 5m45s
Project CI / Native shell tests (push) Successful in 16m17s
主站规范生成支持多张参考图

修复普通生成参考图删除与提交状态不同步

优化触屏设备删除按钮可达性并补充回归测试

---------

Co-authored-by: kdletters <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/164
Co-authored-by: 孟豪 <mh18530625731@163.com>
Co-committed-by: 孟豪 <mh18530625731@163.com>
2026-08-21 11:55:32 +08:00
lhk229 7e3adc38b9 完美像素产物会缩放到与原图相近的尺寸;提高识别小网格的倾向 (#172)
Project CI / Frontend tests (push) Successful in 4m6s
Project CI / Repository checks (push) Successful in 4m14s
Project CI / Backend tests (push) Successful in 5m2s
Project CI / Native shell tests (push) Successful in 17m6s
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/172
Co-authored-by: Linghong <ink29535@proton.me>
Co-committed-by: Linghong <ink29535@proton.me>
2026-08-21 11:19:17 +08:00
Git Hooks Test bacd7a9da7 完成 AGC 直连 Codex 与审核 Skill 工具链
收口首页和项目聊天为唯一 Direct Codex thread

内置五项审核 Skill Pack 与受控 MCP、浏览器及美术工具桥

补齐 Codex Windows 侧车组件、Provider 代理和凭据幂等边界

同步客户端资源投影、交互测试及技术文档
2026-08-20 23:55:00 +08:00
k88936 0797410eef Feat/UI自动识别和布局编辑器简化版 (#170)
Project CI / Repository checks (push) Successful in 3m18s
Project CI / Frontend tests (push) Successful in 3m32s
Project CI / Backend tests (push) Successful in 4m24s
Project CI / Native shell tests (push) Successful in 15m34s
* 支持从项目, 本地电脑, 主站导入/引用素材.
* 页面复杂, 需要足够空间, 作为资源打开时会占满右侧
* 编辑结果作为一种新的美术资源.
* 新增素材按钮现在是空的实现, 临时在旁边加了一个新增UI设计的按钮

临时入口:
![shotmd-1787128060.jpg](/attachments/349a4fa8-7abe-4bbb-80e0-73b1717b6488)

简化:
* 组件实现了最基本的图片和文本
* 容器式布局未接入llm编辑
* 不同阶段统一维护一个状态State, 任何阶段都可以人工调整, 不设单独的步骤
* 多图的UI合并功能暂时不要求, 隐藏入口
* 因为生成工具尚未从主站迁移, 主站地图子画布未实现, 以tab栏的形式容纳素材的子画布需求搁置
* 未实现llm绑定字体功能
* 识别会替换现有UI树, 暂时不是增量的

Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/170
Co-authored-by: 王德宇 <kvtodev@outlook.com>
Co-committed-by: 王德宇 <kvtodev@outlook.com>
2026-08-20 19:07:25 +08:00
kdletters 4bf6c50a56 修复AGC开发者凭据目录权限初始化
Project CI / Repository checks (push) Successful in 2m22s
Project CI / Frontend tests (push) Successful in 2m52s
Project CI / Backend tests (push) Successful in 3m38s
Project CI / Native shell tests (push) Successful in 14m13s
允许客户端自动收紧当前用户持有目录的继承 ACL

保留 foreign owner 与异常路径的失败关闭边界

补充 Windows 回归测试并同步实施决策文档
2026-08-20 14:11:59 +08:00
kdletters 341761ee3b 同步AGC单窗口原生门禁
Project CI / Repository checks (push) Successful in 2m21s
Project CI / Frontend tests (push) Successful in 3m0s
Project CI / Backend tests (push) Successful in 4m5s
Project CI / Native shell tests (push) Successful in 14m20s
禁止普通启动自动打开开发窗口
2026-08-19 19:03:54 +08:00
kdletters 4ba1859f10 修复AGC原生HTTP能力门禁
Project CI / Repository checks (push) Successful in 2m48s
Project CI / Frontend tests (push) Successful in 3m9s
Project CI / Backend tests (push) Successful in 4m15s
Project CI / Native shell tests (push) Failing after 15m21s
同步登录服务器切换后的受控HTTP allowlist

记录Native门禁的现役能力边界
2026-08-19 16:56:16 +08:00
kdletters 925aefd934 修复AGC Native测试夹具
Project CI / Repository checks (push) Successful in 2m46s
Project CI / Frontend tests (push) Successful in 3m10s
Project CI / Backend tests (push) Successful in 4m21s
Project CI / Native shell tests (push) Failing after 14m44s
固定普通 JSON Provider mock 为非流式

为平台账号画布与视觉门禁测试安装隔离会话

同步现役路由、默认配置与提示词断言
2026-08-19 14:42:41 +08:00
kdletters 9fe800da80 修复AGC Native测试认证与阻断
Project CI / Repository checks (push) Successful in 2m33s
Project CI / Frontend tests (push) Successful in 2m55s
Project CI / Backend tests (push) Successful in 3m58s
Project CI / Native shell tests (push) Failing after 16m33s
- 为画布与资源编辑 loopback fixture 增加 task-local External Editor 凭据

- 为平台账号语义测试安装隔离会话并修正资源编辑错误分类断言

- 为 Windows mock listener 增加有界 accept 与 blocking stream 恢复

- 同步 autonomous main-loop、completion fixture 与项目决策记录
2026-08-19 03:24:29 +08:00
kdletters 0fc69c620a 修复Gitea仓库门禁依赖安装
Project CI / Repository checks (push) Successful in 2m21s
Project CI / Frontend tests (push) Successful in 3m6s
Project CI / Backend tests (push) Successful in 3m30s
Project CI / Native shell tests (push) Failing after 3h0m0s
- Repository checks 在运行 AGC AppSurface 前安装子包依赖

- 补充工作流依赖准备契约测试
2026-08-18 22:35:36 +08:00
kdletters b8dc3fe1b1 修复AGC登录网络错误与构建门禁
Project CI / Repository checks (push) Failing after 1m3s
Project CI / Frontend tests (push) Successful in 3m3s
Project CI / Backend tests (push) Successful in 4m3s
Project CI / Native shell tests (push) Failing after 3h0m0s
- 登录页归一网络错误并避免暴露底层 transport 文本

- 补充拒绝连接提示与错误信息脱敏回归测试

- master 推送门禁增加 AGC AppSurface 验证
2026-08-18 22:15:49 +08:00
kdletters fe69a49a9c 修正AGC主分支门禁
Project CI / Repository checks (push) Successful in 1m23s
Project CI / Frontend tests (push) Failing after 2m8s
Project CI / Native shell tests (push) Failing after 3m5s
Project CI / Backend tests (push) Successful in 3m43s
- 修正直连 Codex 烟测脚本导入排序

- 修正播放请求回调依赖并稳定调用最新动作
2026-08-18 21:18:39 +08:00
kdletters 3cda3f3e33 合并AGC直连运行时可靠性修复
- 合并登录服务器切换与本地资源请求修复

- 合并已有游戏意图分流与同线程试玩链路

- 合并直连运行时素材验收与真实浏览器证据改进
2026-08-18 20:58:14 +08:00
kdletters 64310b3719 Merge remote-tracking branch 'origin/master' into codex/agc-runtime-generation-reliability 2026-08-18 20:54:01 +08:00
kdletters f9ee0d5ac7 恢复直连已有游戏意图分流
- 已有游戏默认进入同一 Codex thread 编辑与试玩链路

- 仅明确新建或重做美术请求触发平台素材准备

- 补充已有游戏编辑与外部美术生成意图分流测试
2026-08-18 20:53:13 +08:00
kdletters 9070c0760b 修复登录服务器切换与本地资源请求
- 允许 release 客户端安全访问 custom HTTPS 与本机 loopback HTTP

- 修复 Tauri WebView 请求传输与无效 URLPattern

- 按服务器 origin 隔离本机 External Editor 凭据

- 保留登录请求底层错误并补充路由与凭据隔离测试
2026-08-18 18:32:49 +08:00
kdletters 1e9d74fa4a 增加登录服务器选择
- 登录页支持 release、dev 和 custom 服务器

- 持久化服务器选择并统一登录与平台会话请求地址

- 校验自定义服务器 origin 与 HTTPS 安全边界

- 补充前端、AppSurface 和 Rust 平台会话测试
2026-08-18 13:46:03 +08:00
kdletters bdf7ed288a 放宽直连游戏验收并增加运行时素材证据
- 将固定四切片与固定 drawImage 降为推荐路径

- 在导航前注入 Canvas/WebGL 平台素材运行时观测

- 对缺少核心素材渲染证据的 desktop/mobile 视口回灌同一 Codex thread 整改

- 补充直连验收、浏览器脚本与真实 Chrome fixture 测试

- 同步 AGC 直连 Runtime 实施计划
2026-08-18 13:14:45 +08:00
kdletters 7f60a00c84 修复预览镜像认证入口
Project CI / Repository checks (push) Successful in 1m23s
Project CI / Frontend tests (push) Successful in 2m59s
Project CI / Backend tests (push) Successful in 4m3s
Project CI / Native shell tests (push) Successful in 14m7s
为分支预览启用首次密码登录自动注册

为预览短信入口配置隔离 mock 验证码

补充预览部署门禁和认证验收文档
2026-08-17 22:55:27 +08:00
kdletters 94200a3cdb 修复AGC直连聊天持久化与生成竞态
持久化直连用户消息和最终安全回复,并在重开项目时恢复

补充直连聊天与工作台验收回归测试

直连美术生成和版本登记等待项目写锁,避免首轮生成被聊天保存阻断

同步直连 Runtime 实施计划
2026-08-17 20:11:22 +08:00
kdletters 286c402a4b 修复运行时设置弹窗背景与滚动
设置弹窗打开时锁定页面滚动并在关闭后恢复原状态
将弹窗、提示条和底部状态区改为不透明实色背景
补充设置弹窗滚动锁定与恢复的界面回归测试
2026-08-17 19:03:50 +08:00
kdletters f30e6e9f33 修复智能创作鉴权提示可读性
分隔产品名称与直连运行时错误原因

同步更新直连错误投影测试
2026-08-17 15:35:13 +08:00
kdletters 7ecc9f4971 合并平台账号编辑器 API 分支
登录态接入平台编辑器生成接口

普通用户隐藏外部编辑器高级配置

保留陶泥儿直连运行时生成反馈
2026-08-17 15:20:19 +08:00
kdletters 836d599a98 Merge remote-tracking branch 'origin/master' into codex/agc-runtime-generation-reliability
# Conflicts:
#	docs/project-memory/shared-memory/decision-log.md
2026-08-17 15:12:43 +08:00
kdletters 5ea453d5d1 收口AGC单窗口启动
取消开发态自动打开 Agent 聊天窗口

统一客户端可见标题为陶泥儿

同步启动守卫、测试与技术文档
2026-08-17 14:32:13 +08:00
kdletters 08bca001a2 修复AGC直连创作反馈与资源预览
直连运行时推送陶泥儿美术、代码与版本登记阶段进度

资源管理首屏预取可预览资源并补齐回归测试

同步直连运行时实施计划和团队决策记录
2026-08-17 13:17:14 +08:00
kdletters 91c333ea4a 修复 Windows 微信支付测试的 OpenSSL 构建依赖
移除 platform-wechat 的 openssl 测试依赖

使用固定 RSA PEM fixture 保持签名与解析测试语义

记录 Windows MSVC 原生 OpenSSL 测试依赖陷阱
2026-08-17 11:34:15 +08:00
kdletters 81a35e0bff 让AGC登录后直接使用平台编辑器API
普通客户端使用陶泥儿登录态访问站内编辑器、素材与生成状态路由
隔离standalone高级模式的External URL与Developer API Key配置
同步GUI与Runner的平台会话、可信服务origin和鉴权代际
绑定生成账本owner并阻断登出或换号后的旧请求落盘
修正本地图片上传确认、项目资源登记和sourceReferenceId链路
区分401登录刷新与403权限不足并保留原幂等恢复语义
补充后端客户端幂等命名空间、稳定结果投影和图集请求头
更新AGC界面测试、Rust契约测试及项目技术文档
2026-08-17 10:43:08 +08:00
kdletters 647cf82cec 实现AGC直连运行时与陶泥儿美术闭环
新增直连 Codex 项目聊天、会话恢复与可读错误反馈

接入陶泥儿规范图、背景、固定四切片并校验实际渲染

同步游戏代码资源分类、项目版本 revision 与外部编辑器契约
2026-08-17 09:57:31 +08:00
kdletters f69d9a2e11 Merge remote-tracking branch 'origin/master' into codex/agc-runtime-generation-reliability
# Conflicts:
#	apps/ai-game-creator-shell/src-tauri/src/project/manifest.rs
#	apps/ai-game-creator-shell/src/features/app-shell/useHomeProjectCreation.ts
#	apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts
2026-08-15 15:20:30 +08:00
kdletters dedff81475 修复AGC无人值守生成阻断的交付收口与验收
- 可信 code-prototype 父 Run 认领并观察直属美术 delivery,普通失败立即收束,合法安全默认 marker 由 Runtime 确定性执行唯一同合同返工

- 补齐 suppressed 无 child 与父身份链丢失时的 completion 失败关闭边界

- 修复 Windows project.lock delete-pending 竞争并收紧 HTML 内联 JS 语法 fail-open

- 同步技术方案、决策记录与实施计划,并完成确定性及真实 Provider 分层验证
2026-08-15 15:12:05 +08:00
kdletters 02fff8f308 修复AGC无人值守游戏生成阻断
普通Web工作台默认使用单Supervisor自主生成链路

补齐安全默认返工、结构化诊断与同Run恢复

绑定当前revision、入口摘要和双视口试玩完成凭证

增强Runner跨boot单飞恢复与game-chat来源隔离

新增真实Provider验收入口及确定性回归

同步更新技术方案、开发流程和共享决策
2026-08-14 07:14:25 +08:00
kdletters 7774642365 文档:制定AGC无人值守游戏生成修复计划
明确普通GUI单主快车道路由与无人值守安全边界

定义验证自修复、产物完成门和Runner恢复对账合同

补充端到端验收矩阵、验证命令与回滚边界
2026-08-13 22:21:59 +08:00
892 changed files with 165040 additions and 93664 deletions
@@ -27,7 +27,7 @@ Prefer `scripts/genarrative_external_api.py` for runnable REST calls. It uses on
## Essential Invariants
- Authenticate MCP and business API calls with `Authorization: Bearer <tnr_sk_...>`. Never ask the user to paste a key into chat or place one in repository files.
- All eight generation POST routes require `Idempotency-Key` and return HTTP `202`; `202` is durable acceptance, not a media result.
- All nine generation POST routes require `Idempotency-Key` and return HTTP `202`; `202` is durable acceptance, not a media result.
- Retry an uncertain submission only with the exact same body and the same idempotency key. A polling timeout is not permission to generate again.
- 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.
@@ -106,6 +106,21 @@ client.generate_image(
)
```
For background removal, pass a stable owner-scoped object key, project resource ID, or asset ID; the helper keeps the same asynchronous submission and polling contract:
```python
session = client.prepare_canvas_session("去背景画布")
client.remove_background(
"editor-upload/object.png",
source_width=720,
source_height=1280,
canvasSession=session,
assetLabel="去背景结果",
)
```
Background removal preserves the source pixel size. For normal canvas placement with `canvasSession`, pass the real `source_width` and `source_height`, or provide both `canvasWidth` and `canvasHeight`; the helper rejects missing dimensions instead of guessing a square placeholder. `assetKind` may only describe a static image and must match the authoritative source record. Prefer a project resource ID or asset ID when the same object key has multiple semantic registrations; for a raw object key outside in-place replacement, pass `sourceResourceId` to disambiguate. Passing `targetLayerId` selects in-place replacement: the helper retains the session's project/library context but does not inject `canvasCompletion`, and it rejects an explicit `canvasCompletion` combined with `targetLayerId`. The target layer must point to the same authoritative object as the source, and the server durably binds a raw object key to that target resource for Worker revalidation.
Helper convenience methods wait locally, but the server still uses short asynchronous submit/status requests. For durable caller-controlled orchestration, call `submit_generation`, persist its `operationId` and idempotency key, then call `get_generation` or `wait_for_generation`.
For character animation, pass the canvas session and asset label to `animate_character`. The helper submits asynchronously and returns the completed compact result containing the authoritative formal `resource` and `asset`; do not synthesize a library asset from the first frame.
@@ -51,14 +51,15 @@ Every generation row requires a stable `Idempotency-Key` header and returns HTTP
| --- | --- | --- | --- |
| Image generation | `/api/external/v1/editor/images/generations` | `prompt` | `kind`, `style`, `model`, `aspectRatio`, `imageSize`, `size`, `referenceImageSrcs`, `projectId`, `assetFolderId`, `assetLabel`, `canvasCompletion`, `generationInputs` |
| Image edit/redraw | `/api/external/v1/editor/images/edits` | `prompt`, `sourceReferenceId` | `referenceImageSrcs`, `model`, `size`, `projectId`, `assetFolderId`, `assetLabel`, `targetLayerId`, `canvasCompletion` |
| Icon spritesheet | `/api/external/v1/editor/icon-spritesheets/generations` | `referenceId`, `iconDescriptions` | `style`, `referenceImageSrcs`, `screenColor`, `model`, `aspectRatio`, `imageSize`, `projectId`, `assetFolderId`, `assetLabel`, `canvasCompletion` |
| Background removal | `/api/external/v1/editor/images/background-removals` | `sourceImageSrc` | `projectId`, `sourceResourceId`, `targetLayerId`, static-image `assetKind`, `assetFolderId`, `assetLabel`, `canvasCompletion`, `generationInputs` |
| Icon spritesheet | `/api/external/v1/editor/icon-spritesheets/generations` | `referenceId`, `iconDescriptions` | `sliceLayout`, `style`, `referenceImageSrcs`, `screenColor`, `model`, `aspectRatio`, `imageSize`, `projectId`, `assetFolderId`, `assetLabel`, `canvasCompletion` |
| UI asset extraction | `/api/external/v1/editor/ui-designs/assets/extractions` | `sourceImageSrc`, `aspectRatio`, `imageSize` | `screenColor`, `model`, `referenceImageSrcs`, `projectId`, `assetFolderId`, `spritesheetLabel`, `canvasCompletion` |
| Character animation | `/api/external/v1/editor/character-animations/generations` | `sourceLayerId`, `sourceImageSrc`, `sourceWidth`, `sourceHeight`, `promptText`, `resolution`, `ratio`, `frameCount`, `durationSeconds`, `model` | `projectId`, `sourceResourceId`, `assetFolderId`, `assetLabel`, `canvasCompletion` |
| Video generation | `/api/external/v1/editor/videos/generations` | `prompt`, `model`, `aspectRatio`, `durationSeconds`, `resolution`, `mode`, `sound` | `referenceImageSrcs`, `referenceVideoSrcs`, `referenceAudioSrcs`, `webSearchEnabled`, `projectId`, `assetFolderId`, `assetLabel`, `canvasCompletion` |
| Sound effect | `/api/external/v1/editor/audios/sound-effects/generations` | `prompt` | `model`, `duration`, `loop`, `projectId`, `assetFolderId`, `assetLabel`, `canvasCompletion`, `generationInputs` |
| Background music | `/api/external/v1/editor/audios/background-music/generations` | `gptDescriptionPrompt`, `makeInstrumental` | `projectId`, `assetFolderId`, `assetLabel`, `canvasCompletion`, `generationInputs` |
Poll all eight through:
Poll all nine through:
```text
GET /api/external/v1/generations/{operationId}
@@ -72,6 +73,7 @@ Supply the `operationId` returned by submission. Poll no faster than `pollAfterM
- Pass `assetFolderId` plus `assetLabel` for image, edit, icon spritesheet, video, sound effect, and BGM operations when supported.
- UI extraction uses `assetFolderId` and `spritesheetLabel`.
- Character animation accepts `assetFolderId` and `assetLabel`. Its completed compact result directly returns the final `assetKind="character-animation"` resource and asset with `imageSequenceFrames` and `imageSequenceDurationMs`; never create a duplicate first-frame resource or asset.
- Background removal derives the final static-image `assetKind` from the authoritative source record. A conflicting request kind or any video, audio, animation, or image-sequence kind returns `400` before queueing. Without `canvasCompletion`, `targetLayerId` must point to the same authoritative object as `sourceImageSrc` (prefer `assetObjectId`, otherwise canonical bucket/object key).
- If a caller must manually create a `character-animation` resource or asset, put the authoritative frames and total sequence duration in `imageSequenceFrames` and `imageSequenceDurationMs`. Keep `generationInputs` replayable: it must not contain legacy runtime fields such as `characterAnimation`, `frames`, `previewVideoPath`, `frameCount`, `fps`, or `durationSeconds`.
- Reload project/library state after completion when full current state is required.
@@ -92,6 +94,8 @@ 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.
`sliceLayout: "grid-2x2"` is an opt-in contract for four fixed game-runtime assets. The provider prompt and server persistence both preserve the ordered slots left-top, right-top, left-bottom, right-bottom. Omit it to retain the default connected-component slicing behaviour for ordinary free-form icon sheets.
## Common Values
Use OpenAPI as the final authority; these common values are a routing aid:
@@ -47,6 +47,7 @@ Infer what is already clear and ask only for missing fields that block the selec
| --- | --- |
| Generate a background, character, spec, UI mockup, or publication image | Image generation |
| Redraw, retouch, or replace an existing image | Image edit |
| Remove the background from an existing image | Background removal |
| Generate from a local reference | Upload and confirm the local file, then image generation or edit |
| Build a reusable transparent icon/game atlas from a visual spec | Icon spritesheet generation |
| Extract marked assets from an existing UI design | UI design asset extraction |
@@ -78,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.
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`.
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 the four-category game contract it must also send `sliceLayout: "grid-2x2"`; this is an explicit fixed-slot contract, not a client-side guessed crop.
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. For the four-category game-chat contract, require 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. For the four-category game-chat contract, require response `sliceLayout: "grid-2x2"` 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.
Never use `assets/ui-prototype.png` as the spritesheet visual-spec reference. UI extraction is outside this canonical DAG.
@@ -15,7 +15,7 @@ Use this reference to build generation payloads, carry canvas/library context, p
## Asynchronous Submission
All eight generation POST routes require `Idempotency-Key` and return HTTP `202` with an `ExternalEditorGenerationSubmissionResponse` shaped like:
All nine generation POST routes require `Idempotency-Key` and return HTTP `202` with an `ExternalEditorGenerationSubmissionResponse` shaped like:
```json
{
@@ -71,6 +71,8 @@ status = client.get_generation(operation_id)
completed = client.wait_for_generation(operation_id)
```
Background removal uses the same submission and polling state machine. `sourceImageSrc` must be a stable owner-scoped object key, project resource ID, or asset ID; never pass a Data URL, Blob URL, or expiring signed URL. An explicit resource ID or asset ID is resolved before any object-key fallback. If a raw object key has multiple registrations with conflicting authoritative metadata, pass `sourceResourceId` to disambiguate or the server returns `400`. Use `projectId + canvasCompletion` for normal canvas placement. When `canvasCompletion` is absent, `projectId + targetLayerId` replaces an existing resource-backed layer and is rejected before queueing if the target is invalid; for a raw object key, the target resource becomes the durable source binding rechecked by the Worker. If both placement fields are absent, the server does not add the result to the canvas. The completed compact result contains the stable output object key, dimensions, and persisted resource/asset references when requested.
## Canvas and Asset-Library Completion
For endpoints that support these fields, include:
@@ -98,6 +100,8 @@ A minimal `canvasCompletion` is:
`dialogId` is optional. The placeholder supplies canvas placement and completion coordinates; it is not a final media pixel-size constraint. For successful pixel-art snapping, the result layer uses the final logical-grid PNG dimensions even when they differ from the placeholder. Do not reconstruct canvas state from completion results. Reload the project and asset library when complete authoritative snapshots are needed.
Background removal preserves the source image dimensions. For normal canvas placement, the Python helper therefore requires the real `source_width` and `source_height` whenever `canvasSession` is used without an explicit `canvasWidth` plus `canvasHeight`; it never substitutes a square default. Passing `targetLayerId` instead selects in-place replacement, so the helper keeps the session's project/library fields without injecting `canvasCompletion` and rejects callers that explicitly combine both placement modes. The request `assetKind` is optional, static-image only, and must equal the authoritative source type when one exists. An in-place target must resolve to the same authoritative source object; a raw object key is bound to that target resource instead of relying on project-list order.
Character animation accepts `assetFolderId` and `assetLabel` and persists the final transparent sequence directly. Its completed compact result includes the authoritative `assetKind="character-animation"` resource and asset with `imageSequenceFrames` and `imageSequenceDurationMs`. Use those records directly and never synthesize a duplicate asset from the first frame.
For the lower-level asset/resource creation endpoints, `generationInputs` is replayable request context rather than a media-runtime container. When `assetKind` is `character-animation`, the server rejects legacy runtime keys including `characterAnimation`, `frames`, `previewVideoPath`, `frameCount`, `fps`, and `durationSeconds`; send the formal sequence through `imageSequenceFrames` and `imageSequenceDurationMs`. Internal processing audit keys such as `screenColorHex`, `mattingProvider`, and `mattingModel` are removed before persistence.
@@ -561,6 +561,58 @@ class GenarrativeExternalClient:
idempotency_key=idempotency_key,
)
def remove_background(
self,
source_image_src: str,
source_width: int | None = None,
source_height: int | None = None,
**fields: Any,
) -> Any:
source_image_src = normalize_optional_text(source_image_src)
if not source_image_src:
raise GenarrativeApiError(
"source_image_src must be an owner-scoped object key, resource ID, or asset ID"
)
if (source_width is None) != (source_height is None):
raise GenarrativeApiError("source_width and source_height must be provided together")
if source_width is not None and (
source_width <= 0 or source_height is None or source_height <= 0
):
raise GenarrativeApiError("source_width and source_height must be positive integers")
session = fields.get("canvasSession")
if session is None:
session = fields.get("canvas_session")
target_layer_id = normalize_optional_text(fields.get("targetLayerId"))
if target_layer_id and fields.get("canvasCompletion") is not None:
raise GenarrativeApiError(
"targetLayerId and canvasCompletion are mutually exclusive for background removal"
)
canvas_width = fields.get("canvasWidth")
canvas_height = fields.get("canvasHeight")
if (canvas_width is None) != (canvas_height is None):
raise GenarrativeApiError("canvasWidth and canvasHeight must be provided together")
if session is not None and canvas_width is None and not target_layer_id:
if source_width is None or source_height is None:
raise GenarrativeApiError(
"remove_background requires source_width and source_height when canvasSession is used without canvasWidth/canvasHeight"
)
fields["canvasWidth"] = source_width
fields["canvasHeight"] = source_height
self._apply_canvas_session_fields(
fields,
fields.get("assetLabel", "去背景结果"),
source_width or 1,
source_height or 1,
)
if target_layer_id:
fields.pop("canvasCompletion", None)
idempotency_key = fields.pop("idempotencyKey", None)
return self.submit_and_wait_generation(
"/api/external/v1/editor/images/background-removals",
{"sourceImageSrc": source_image_src, **fields},
idempotency_key=idempotency_key,
)
def generate_icon_spritesheet(
self,
reference_id: str,
@@ -687,11 +739,14 @@ def _self_test() -> None:
b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR"
b"\x00\x00\x00\x02\x00\x00\x00\x03\x08\x06\x00\x00\x00"
)
with tempfile.NamedTemporaryFile(suffix="Hero Image.png") as fh:
with tempfile.NamedTemporaryFile(suffix="Hero Image.png", delete=False) as fh:
fh.write(png)
fh.flush()
assert image_dimensions(fh.name) == (2, 3)
assert source_layer_id_from_path(fh.name).startswith("external-reference-")
temp_path = fh.name
try:
assert image_dimensions(temp_path) == (2, 3)
assert source_layer_id_from_path(temp_path).startswith("external-reference-")
finally:
Path(temp_path).unlink(missing_ok=True)
assert unwrap_envelope({"ok": True, "data": {"upload": 1}}) == {"upload": 1}
client = GenarrativeExternalClient(api_key="test")
session = {"projectId": "proj-demo", "assetFolderId": "editor-asset-folder-demo"}
@@ -773,6 +828,54 @@ def _self_test() -> None:
assert len(result["asset"]["imageSequenceFrames"]) == 2
assert result["asset"]["imageSequenceDurationMs"] == 4000
calls.clear()
background_result = client.remove_background(
"uploads/source.png",
720,
1280,
canvasSession=session,
assetLabel="去背景结果",
)
assert background_result["taskId"] == "task-demo"
assert calls[0]["path"] == "/api/external/v1/editor/images/background-removals"
assert calls[0]["body"]["sourceImageSrc"] == "uploads/source.png"
assert calls[0]["body"]["projectId"] == "proj-demo"
assert calls[0]["body"]["assetFolderId"] == "editor-asset-folder-demo"
assert calls[0]["body"]["assetLabel"] == "去背景结果"
assert calls[0]["body"]["canvasCompletion"]["title"] == "去背景结果"
assert calls[0]["body"]["canvasCompletion"]["placeholder"]["width"] == 720
assert calls[0]["body"]["canvasCompletion"]["placeholder"]["height"] == 1280
calls.clear()
client.remove_background(
"uploads/source.png",
canvasSession=session,
targetLayerId="layer-1",
assetLabel="原位去背景结果",
)
assert calls[0]["body"]["projectId"] == "proj-demo"
assert calls[0]["body"]["assetFolderId"] == "editor-asset-folder-demo"
assert calls[0]["body"]["assetLabel"] == "原位去背景结果"
assert calls[0]["body"]["targetLayerId"] == "layer-1"
assert "canvasCompletion" not in calls[0]["body"]
calls.clear()
try:
client.remove_background(
"uploads/source.png",
canvasSession=session,
targetLayerId="layer-1",
canvasCompletion={"title": "冲突完成指令"},
)
except GenarrativeApiError as error:
assert "targetLayerId and canvasCompletion are mutually exclusive" in str(error)
else:
raise AssertionError("background removal must reject conflicting canvas placement modes")
assert calls == []
try:
client.remove_background("uploads/source.png", canvasSession=session)
except GenarrativeApiError as error:
assert "source_width and source_height" in str(error)
else:
raise AssertionError("canvas background removal must not guess source dimensions")
assert calls == []
client.generate_icon_spritesheet(
"editor-resource-spec",
["蛇头向上", "蛇身直线", "转角", "尾部", "四类食物"],
+1
View File
@@ -163,6 +163,7 @@ module.exports = {
'server-rs/target',
'server-rs/target-*',
'apps/desktop-shell/src-tauri/target',
'apps/ai-game-creator-shell/src/features/ui-editor/types/**',
'target',
'src/main.tsx',
'src/App.tsx',
+21 -7
View File
@@ -4,7 +4,6 @@ on:
push:
branches:
- master
- codex/ai-game-creator-app
pull_request:
workflow_dispatch:
@@ -59,6 +58,16 @@ jobs:
else
base_ref="$(git merge-base HEAD origin/master 2>/dev/null || git rev-parse HEAD)"
fi
resolved_base_ref="$(git rev-parse --verify "${base_ref}^{commit}" 2>/dev/null || true)"
head_ref="$(git rev-parse HEAD)"
if [[ "${resolved_base_ref}" == "${head_ref}" ]]; then
resolved_base_ref="$(git rev-parse --verify HEAD^ 2>/dev/null || true)"
fi
if [[ -z "${resolved_base_ref}" ]]; then
echo 'comparison base must resolve to a commit distinct from HEAD.' >&2
exit 1
fi
base_ref="${resolved_base_ref}"
if [[ "${GITHUB_EVENT_NAME:-}" == 'pull_request' ]] \
&& ! git merge-base --is-ancestor "${base_ref}" HEAD; then
echo 'pull request head does not contain the latest base commit; update the branch and rerun CI.' >&2
@@ -88,9 +97,6 @@ jobs:
- name: Install npm dependencies
run: bash scripts/ci-npm-ci-with-retry.sh
- name: Install AI game creator dependencies
run: bash scripts/ci-npm-ci-with-retry.sh --prefix apps/ai-game-creator-shell
- name: Run frontend and script tests
run: npm run test
@@ -136,6 +142,16 @@ jobs:
else
base_ref="$(git merge-base HEAD origin/master 2>/dev/null || git rev-parse HEAD)"
fi
resolved_base_ref="$(git rev-parse --verify "${base_ref}^{commit}" 2>/dev/null || true)"
head_ref="$(git rev-parse HEAD)"
if [[ "${resolved_base_ref}" == "${head_ref}" ]]; then
resolved_base_ref="$(git rev-parse --verify HEAD^ 2>/dev/null || true)"
fi
if [[ -z "${resolved_base_ref}" ]]; then
echo 'comparison base must resolve to a commit distinct from HEAD.' >&2
exit 1
fi
base_ref="${resolved_base_ref}"
if [[ "${GITHUB_EVENT_NAME:-}" == 'pull_request' ]] \
&& ! git merge-base --is-ancestor "${base_ref}" HEAD; then
echo 'pull request head does not contain the latest base commit; update the branch and rerun CI.' >&2
@@ -191,14 +207,12 @@ jobs:
- name: Install npm dependencies
run: bash scripts/ci-npm-ci-with-retry.sh
- name: Install AI game creator dependencies
run: bash scripts/ci-npm-ci-with-retry.sh --prefix apps/ai-game-creator-shell
- name: Prepare native Rust dependencies
shell: bash
run: |
set -euo pipefail
for manifest_path in \
server-rs/Cargo.toml \
apps/desktop-shell/src-tauri/Cargo.toml \
apps/ai-game-creator-shell/src-tauri/Cargo.toml; do
for attempt in $(seq 1 5); do
+8
View File
@@ -34,6 +34,12 @@ temp*build*/
/apps/ai-game-creator-shell/src-tauri/target/
/apps/ai-game-creator-shell/src-tauri/gen/
/apps/ai-game-creator-shell/src-tauri/logs/
/apps/ai-game-creator-shell/src-tauri/resources/codex/win-x64/codex.exe
/apps/ai-game-creator-shell/src-tauri/resources/codex/win-x64/manifest.json
/apps/ai-game-creator-shell/src-tauri/resources/codex/win-x64/bin/
/apps/ai-game-creator-shell/src-tauri/resources/codex/win-x64/codex-path/
/apps/ai-game-creator-shell/src-tauri/resources/codex/win-x64/codex-resources/
/apps/ai-game-creator-shell/src-tauri/resources/codex/win-x64/codex-package.json
/apps/ai-game-creator-shell/logs/
/apps/ai-game-creator-shell/.llm-drafts/
/apps/ai-game-creator-shell/game-creator.config.local.json
@@ -48,6 +54,8 @@ temp*build*/
/public/generated-characters
/.codex-temp
/.app/
/.jenkins-source-commit
/.jenkins-spacetime-schema-base
/target/
/logs
/.claude/settings.local.json
@@ -133,7 +133,7 @@ node scripts/dev-stack-port-utils.mjs resolve-dev-stack spacetime:127.0.0.1:0 ap
- [ ] `npm run dev` 的 SpacetimeDB、publish、api-server、主站 Vite、后台 Vite 都使用实际端口。
- [ ] BgFilter worker 在 api-server 前 ready,父子共享实际 base URL / TokenRust watch 只触发一次组合重启。
- [ ] `npm run dev:web` 在主站端口不可用时能切换到可用端口。
- [ ] `npm run agc` / `npm run agc:game-chat` 在 Linux 使用用户段 `start + 5`Tauri、Vite、marker 和预检使用同一最终端口。
- [ ] `npm run agc` 在 Linux 使用用户段 `start + 5`Tauri、Vite、marker 和预检使用同一最终端口。
- [ ] 文档同步更新 `docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`
- [ ] 长期踩坑同步更新 `docs/project-memory/shared-memory/pitfalls.md`
- [ ] 修改中文文件后运行 `npm run check:encoding`
+1
View File
@@ -3,6 +3,7 @@ node_modules
.git
.codex-logs
public/Icons
apps/ai-game-creator-shell/src/features/ui-editor/types/
media
*.log
.preview.*
+1
View File
@@ -26,6 +26,7 @@
- 后续新增 Markdown 文档文件名必须以分类标签开头,格式为 `【标签名】中文标题-日期.md`;历史文档不要求批量重命名,除非本次任务明确涉及。
- 工程修改要同步更新对应 `docs/` 文档;产生长期有效的架构约定、接口变化、排障经验、开发流程或协作规则时,同步更新 `docs/project-memory/shared-memory/`
- 默认保持系统简洁:优先复用、修改、扩展现有系统、页面和公共组件,不新建平行系统或平行页面。
- UI 开发优先复用现有公共组件;发现跨页面或跨端重复的视觉/交互模式时,先抽取到 `packages/shared` 共享组件库并让现有页面迁移使用,禁止在业务页复制同类 UI。共享组件只承载通用表现与交互,不下沉领域规则、后端副作用或正式业务状态。
- 对已明确退役且不存在现役调用方、公开契约、持久化数据、活跃实例或迁移要求的对象,坚持“四不写”:
1. 不写历史兼容代码。
2. 不写用于维持退役行为的防御性兼容测试。
+21 -54
View File
@@ -1,17 +1,14 @@
# AI Native Visual RPG
# Genarrative / 陶泥儿
一个以“AI 叙事 + 本地规则 + 像素演出”为核心的视觉 RPG 原型
一个 AI 原生互动内容与小游戏平台,当前主站聚焦图片画布创作、项目与素材管理,以及账号、钱包和后台管理等平台公共能力
当前已经具备这些主要能力:
- 世界与角色选择
- AI 剧情推进与流式对话
- 战斗演出、NPC 战斗、切磋
- NPC 交易、送礼、求助、招募
- 宝藏交互
- 同伴跟随与战斗
- 游戏主流程内嵌的角色资产工坊、自定义世界实体编辑与角色形象编辑
- 自动存档与继续游戏
- 图片画布编辑、项目与素材管理
- 图片、视频、音频等外部生成任务
- 账号、钱包、充值、兑换码与个人资料
- 后台运营、配置与生产运维工具
- AI 游戏创作独立 App
## 运行
@@ -27,14 +24,13 @@
npm install
```
该命令会按根 `package.json` 的 npm workspaces 一次安装主站、Admin、Mobile、Desktop、AGC、Preview Deployer、内部 packages 与工具依赖;仓库只使用根 `package-lock.json`,不要在子目录单独执行 `npm install` / `npm ci` 或提交嵌套 lockfile。
准备环境变量:
- 复制 `.env.example``.env.local`
- 填入 `LLM_API_KEY` / `ARK_API_KEY`
- 按需设置 `VITE_LLM_MODEL`
- 如需启用阿里云短信验证码登录,填写 `ALIYUN_SMS_ACCESS_KEY_ID``ALIYUN_SMS_ACCESS_KEY_SECRET`,并确认 `SMS_AUTH_PROVIDER="aliyun"`
- 本地联调短信登录时,建议将 `VITE_AUTH_ALLOW_DEV_GUEST` 设为 `false`,避免开发模式自动进入游客账号而跳过登录页
- 如需打印完整 prompt/output,可把 `VITE_LLM_DEBUG_LOG` 设为 `true`
- 只填写本次联调所需配置;不要提交 `.env.local`、密钥、Token 或其它本地认证信息
- api-server 环境变量和 Provider 配置以 `.env.example` 及当前开发运维文档为准
启动开发环境:
@@ -69,59 +65,30 @@ npm run lint
npm run check:encoding
```
内容引用校验:
```bash
npm run check:data
```
编辑器 override 校验:
```bash
npm run check:overrides
```
关键内容 smoke 检查:
```bash
npm run check:smoke
```
一键内容检查:
```bash
npm run check:content
```
## 主要结构
主运行时:
- [src/App.tsx](./src/App.tsx)
- [src/active-main.tsx](./src/active-main.tsx)
- [src/ActiveApp.tsx](./src/ActiveApp.tsx)
- [src/AuthenticatedApp.tsx](./src/AuthenticatedApp.tsx)
- [src/routing/appRoutes.tsx](./src/routing/appRoutes.tsx)
- [src/hooks/useCombatFlow.ts](./src/hooks/useCombatFlow.ts)
- [src/routing/activeAppRoutes.tsx](./src/routing/activeAppRoutes.tsx)
- [src/components/platform-entry/PlatformEntryActiveFlowShell.tsx](./src/components/platform-entry/PlatformEntryActiveFlowShell.tsx)
主流程内嵌编辑能力:
创作与项目能力:
- [src/components/image-editor/ImageCanvasEditorView.tsx](./src/components/image-editor/ImageCanvasEditorView.tsx)
- [src/components/rpg-creation-editor/RpgCreationEntityEditorModal.tsx](./src/components/rpg-creation-editor/RpgCreationEntityEditorModal.tsx)
- [src/components/rpg-creation-asset-studio/RpgCreationRoleAssetStudioModal.tsx](./src/components/rpg-creation-asset-studio/RpgCreationRoleAssetStudioModal.tsx)
核心数据:
- [src/data/scenePresets.ts](./src/data/scenePresets.ts)
- [src/data/characterPresets.ts](./src/data/characterPresets.ts)
- [src/data/npcInteractions.ts](./src/data/npcInteractions.ts)
- [src/data/treasureInteractions.ts](./src/data/treasureInteractions.ts)
- [src/components/creation-home/CreationLandingView.tsx](./src/components/creation-home/CreationLandingView.tsx)
- [src/components/project/ProjectGalleryView.tsx](./src/components/project/ProjectGalleryView.tsx)
## 文档入口
`docs/` 已在 `2026-05-15` 完成压缩整理,旧 PRD、设计、审计、阶段计划和技术流水账不再作为实现依据。当前只读取
`docs/` 已在 `2026-08-25` 按当前代码与运行态重新收口。旧 PRD、设计、审计、阶段计划和技术流水账不再作为实现依据;专题文档的现役清单统一从 `docs/README.md` 进入
- [docs/README.md](./docs/README.md):当前文档总入口。
- [docs/【项目基线】当前产品与工程约束-2026-05-15.md](./docs/【项目基线】当前产品与工程约束-2026-05-15.md):产品、命名、UI、协作和废弃路线。
- [docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md](./docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md)DDD 边界、API 分组、SpacetimeDB schema 规则和表目录。
- [docs/【玩法创作】平台入口与玩法链路-2026-05-15.md](./docs/【玩法创作】平台入口与玩法链路-2026-05-15.md)创作入口、草稿架和各玩法当前口径
- [docs/【玩法创作】平台入口与玩法链路-2026-05-15.md](./docs/【玩法创作】平台入口与玩法链路-2026-05-15.md)平台现役入口、项目页和画布链路
- [docs/【开发运维】本地开发验证与生产运维-2026-05-15.md](./docs/【开发运维】本地开发验证与生产运维-2026-05-15.md):本地启动、检查、部署、埋点和运营查询。
- [docs/project-memory/README.md](./docs/project-memory/README.md):团队共享的当前项目记忆、决策和未关闭事项。
- [UI_CODING_STANDARD.md](./UI_CODING_STANDARD.md):像素 UI 资产与编码规范。
-177
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,6 +1,6 @@
# UI Coding Standard
> **当前文档入口**:项目文档已压缩到 `docs/README.md` 和 4 份当前文档UI 资产和 9-slice 规则以本文为准,平台级 UI 约束见 `docs/【项目基线】当前产品与工程约束-2026-05-15.md`。
> **当前文档入口**:项目文档总入口为 `docs/README.md`UI 资产和 9-slice 规则以本文为准,平台级 UI 约束见 `docs/【项目基线】当前产品与工程约束-2026-05-15.md`,专题方案只从总入口读取
## Goal
+5 -1
View File
@@ -10,6 +10,7 @@
"preview": "vite preview --host 127.0.0.1"
},
"dependencies": {
"@genarrative/shared": "0.1.0",
"@vitejs/plugin-react": "^5.0.4",
"lucide-react": "^0.546.0",
"react": "^19.0.0",
@@ -17,8 +18,11 @@
"vite": "^6.2.0"
},
"devDependencies": {
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"typescript": "~5.8.2"
"typescript": "~5.8.2",
"vitest": "^0.34.6"
}
}
@@ -2,11 +2,11 @@
"agentMode": "codex_app_server",
"llm": {
"apiKey": "",
"baseUrl": "https://api.openai.com/v1",
"model": "gpt-4.1",
"baseUrl": "https://dev.genarrative.world/gpt/v1",
"model": "gpt-5.6-sol",
"apiKind": "openai_responses",
"reasoningEffort": "high",
"stream": false,
"reasoningEffort": "max",
"stream": true,
"webSearchEnabled": false,
"contextWindowTokens": 128000,
"autoCompactTokenLimit": 64000,
@@ -16,9 +16,7 @@
"retryBackoffMs": 500
},
"agentLlm": {},
"editorApi": {
"baseUrl": "http://127.0.0.1:8082",
"apiKey": ""
},
"mcpServers": {}
"planning": {
"capabilityEnabled": true
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="data:," />
<title>AI 游戏创作</title>
<title>陶泥儿</title>
</head>
<body>
<div id="root"></div>
File diff suppressed because it is too large Load Diff
+17 -5
View File
@@ -1,15 +1,13 @@
{
"name": "@genarrative/ai-game-creator-shell",
"private": true,
"version": "0.1.0",
"version": "0.1.8",
"type": "module",
"scripts": {
"dev": "node scripts/start-tauri-dev.mjs",
"game-chat": "node scripts/start-tauri-dev.mjs --game-chat",
"dev-server": "node scripts/start-dev-server.mjs",
"dev-stack": "node scripts/start-dev-stack.mjs",
"build": "npm --prefix ../.. exec tauri -- build",
"build:game-chat-release": "npm --prefix ../.. exec tauri -- build --config src-tauri/tauri.game-chat-release.conf.json --bundles nsis --features game-chat-release",
"llm-status": "node scripts/run-cli-with-config.mjs --llm-status",
"agent-task": "node scripts/run-cli-with-config.mjs --agent-task",
"chat": "node scripts/run-cli-with-config.mjs --swarm-chat",
@@ -17,6 +15,8 @@
"config": "node scripts/game-creator-config-wizard.mjs",
"test:chat": "node scripts/agent-swarm-test-chat.mjs --task \"制作一个可直接试玩的原创植物塔防小游戏:玩家选择并放置原创守卫阻挡敌人,完成波次后可以进入下一关并重新开始。主题、单位名称与视觉语言必须原创,不使用任何现有游戏角色、单位名、Logo 或受保护视觉语言。请自主完成正式产物、静态检查和双视口试玩验证。\" --no-open",
"test:chat:manual": "node scripts/agent-swarm-test-chat.mjs",
"test:plan": "node scripts/agent-swarm-test-chat.mjs --plan --task \"我想做一款原创横版像素解谜小游戏,主角是一个能操控自己影子的小机器人,影子可以变成平台和开关。请完成立项策划并给出 Fast GDD。主题、角色名与视觉语言必须原创,不使用任何现有游戏角色、名称、Logo 或受保护视觉语言。\"",
"test:plan:manual": "node scripts/agent-swarm-test-chat.mjs --plan",
"agent-run": "node scripts/run-cli-with-config.mjs --agent-run",
"agent-run:smoke": "node scripts/smoke-agent-run-local-provider.mjs",
"agent-runtime:real-e2e": "node scripts/agent-runtime-real-e2e.mjs",
@@ -31,19 +31,27 @@
"agent-runtime:supervisor-swarm-tool-plan-handoff-runner-kill-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-tool-plan-handoff-runner-kill",
"agent-runtime:steer-real-e2e": "node scripts/agent-runtime-steer-real-e2e.mjs",
"agent-runtime:steer-runner-kill-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite steer-runner-kill",
"typecheck": "node ../../node_modules/typescript/bin/tsc -p tsconfig.json --noEmit && node scripts/check-config.mjs"
"typecheck": "tsc -p tsconfig.json --noEmit && node scripts/check-config.mjs"
},
"dependencies": {
"@cubone/react-file-manager": "^1.35.0",
"@genarrative/image-canvas-core": "0.1.0",
"@genarrative/image-canvas-react": "0.1.0",
"@genarrative/shared": "0.1.0",
"@lexical/react": "^0.47.0",
"@lexical/utils": "^0.47.0",
"@tauri-apps/api": "^2.11.1",
"@tauri-apps/plugin-clipboard-manager": "2.3.2",
"@tauri-apps/plugin-dialog": "^2.7.2",
"@tauri-apps/plugin-http": "^2.5.9",
"@tauri-apps/plugin-opener": "~2",
"@vitejs/plugin-react": "^5.0.4",
"focus-trap-react": "^12.0.3",
"lexical": "^0.47.0",
"lucide-react": "^0.546.0",
"react": "^19.0.0",
"react-arborist": "^3.16.0",
"react-colorful": "^5.8.0",
"react-dom": "^19.0.0",
"react-markdown": "^10.1.0",
"remark-gfm": "^4.0.1",
@@ -51,11 +59,15 @@
"zustand": "^5.0.14"
},
"devDependencies": {
"@openai/codex": "0.147.0",
"@tailwindcss/vite": "^4.1.14",
"@tauri-apps/cli": "^2.11.2",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"tailwindcss": "^4.1.14",
"typescript": "~5.8.2"
"typescript": "~5.8.2",
"vitest": "^0.34.6"
}
}
File diff suppressed because it is too large Load Diff
@@ -73,8 +73,7 @@ export function isCatalogBoundToolPlanFunctionName(name, protocol) {
if (protocol !== 'native_runtime_tools') return false;
return (
['update_agent_plan', 'respond_to_user'].includes(name) ||
name.startsWith('runtime_tool_') ||
name.startsWith('mcp_tool_')
name.startsWith('runtime_tool_')
);
}
@@ -2406,11 +2405,36 @@ export function isolatedJoinDeliveryTarget(delivery) {
return target;
}
export function finalMessageId(agentId, sessionId, runId) {
const fingerprint = createHash('sha256')
function runtimeMessageCorrelationId(agentId, sessionId, runId) {
return createHash('sha256')
.update(`${agentId}\n${sessionId}\n${runId}`)
.digest('hex');
return `agent-finalization-${fingerprint.slice(0, 32)}`;
}
export function finalMessageId(agentId, sessionId, runId) {
return `agent-finalization-${runtimeMessageCorrelationId(
agentId,
sessionId,
runId,
).slice(0, 32)}`;
}
export function runtimePublicStatusMessageId(
agentId,
sessionId,
runId,
status,
) {
const correlationId = runtimeMessageCorrelationId(
agentId,
sessionId,
runId,
).slice(0, 32);
const statusFingerprint = createHash('sha256')
.update(status)
.digest('hex')
.slice(0, 16);
return `runtime-public-status-${correlationId}-${statusFingerprint}`;
}
export function backgroundTaskMessageId(agentId, sessionId, runId, source) {
@@ -2444,7 +2468,10 @@ export function disposableProjectPathVariants() {
}
export function formalConfigPathVariants() {
return absolutePathVariants(state.options?.configDir);
return absolutePathVariants(
state.options?.configDir,
state.isolatedRunner?.appDataDir,
);
}
export function absolutePathVariants(...values) {
@@ -20,7 +20,15 @@ import {
stopOwnedIsolatedRunner,
} from './harness/app-data.mjs';
import { loadConfig, parseArguments } from './harness/config.mjs';
import { closeInteractiveCli } from './harness/process.mjs';
import {
activeInteractiveCliSessions,
captureOwnedProcessCleanupSnapshot,
closeInteractiveCli,
destroyInteractiveCliOutputStreams,
interactiveCliOutput,
verifyOwnedProcessCleanupSnapshot,
waitForInteractiveCliStdioClose,
} from './harness/process.mjs';
import { checkPrerequisites } from './harness/project.mjs';
import {
buildSummary,
@@ -57,14 +65,6 @@ import {
isGoalRuntimeSuite,
runGoalRuntimeE2e,
} from './suites/goal.mjs';
import {
collectPartialMcpEvidence,
emptyMcpEvidence,
isMcpRuntimeSuite,
mcpPrivateValues,
runMcpRuntimeE2e,
stopMcpHttpFixture,
} from './suites/mcp.mjs';
import {
collectPartialParallelReadEvidence,
emptyParallelReadEvidence,
@@ -175,7 +175,6 @@ if (selfTestRequested) {
if (isContextCompactionSuite()) {
state.evidence = emptyContextCompactionEvidence();
}
if (isMcpRuntimeSuite()) state.evidence = emptyMcpEvidence();
if (isUserInputRuntimeSuite()) state.evidence = emptyUserInputEvidence();
if (isScopedAgentsSuite()) state.evidence = emptyScopedAgentsEvidence();
if (isProjectSkillSuite()) state.evidence = emptyProjectSkillEvidence();
@@ -193,7 +192,6 @@ if (selfTestRequested) {
if (
isWebSearchSuite() ||
isContextCompactionSuite() ||
isMcpRuntimeSuite() ||
isUserInputRuntimeSuite() ||
isScopedAgentsSuite() ||
isProjectSkillSuite() ||
@@ -234,8 +232,6 @@ if (selfTestRequested) {
await runWebSearchE2e();
} else if (isContextCompactionSuite()) {
await runContextCompactionE2e();
} else if (isMcpRuntimeSuite()) {
await runMcpRuntimeE2e();
} else if (isUserInputRuntimeSuite()) {
await runUserInputRuntimeE2e();
} else if (isScopedAgentsSuite()) {
@@ -266,6 +262,21 @@ if (selfTestRequested) {
recordError(error?.code ?? 'unexpected-error', error);
} finally {
state.cleanupInProgress = true;
const stateTrackedInteractiveCliSessions = new Set(
[
state.userInputCliSession,
state.supervisorAutonomousPlayableCliSession,
state.supervisorSwarmCliSession,
].filter(Boolean),
);
const interactiveCliSessions = [
...new Set([
...stateTrackedInteractiveCliSessions,
...activeInteractiveCliSessions,
]),
];
const supervisorAutonomousPlayableCliSession =
state.supervisorAutonomousPlayableCliSession;
if (isUserInputRuntimeSuite() && state.userInputCliSession) {
try {
await closeInteractiveCli(state.userInputCliSession);
@@ -311,25 +322,51 @@ if (selfTestRequested) {
}
state.supervisorSwarmCliSession = null;
}
if (isMcpRuntimeSuite() && state.mcp.httpFixture) {
for (const session of interactiveCliSessions) {
if (stateTrackedInteractiveCliSessions.has(session)) continue;
try {
await stopMcpHttpFixture();
state.evidence.httpFixtureStopped = true;
await closeInteractiveCli(session);
} catch (error) {
state.status = 'FAIL';
recordError('mcp-http-fixture-cleanup-failed', error);
recordError('interactive-cli-cleanup-failed', error);
}
}
if (
isSupervisorAutonomousPlayableLaneDefenseSuite() &&
state.isolatedRunner.appDataDir
) {
try {
const runnerPid = state.isolatedRunner.current?.pid ?? null;
const helperPid =
state.isolatedRunner.current?.killHandle?.child?.pid ?? null;
state.supervisorAutonomousPlayable.ownedProcessCleanupSnapshot =
await captureOwnedProcessCleanupSnapshot({
runnerPid,
helperPids: Number.isSafeInteger(helperPid) ? [helperPid] : [],
rootPids: [
...interactiveCliSessions.map((session) => session.child?.pid),
...[...activeCommandChildren].map((child) => child.pid),
].filter((pid) => Number.isSafeInteger(pid) && pid > 0),
});
const observed =
state.supervisorAutonomousPlayable.ownedProcessCleanupSnapshot
.observedCounts;
assert(
observed.runner === 1 && observed.helper === 1,
'supervisor-autonomous-playable-owned-process-snapshot-incomplete',
);
} catch (error) {
state.status = 'FAIL';
recordError(
'supervisor-autonomous-playable-owned-process-snapshot-failed',
error,
);
}
}
if (isIsolatedRunnerSuite() && state.isolatedRunner.appDataDir) {
try {
await stopOwnedIsolatedRunner();
state.isolatedRunner.stopped = true;
state.isolatedRunner.cleanupPerformed =
await removeIsolatedSuiteAppData();
if (!state.isolatedRunner.cleanupPerformed) {
state.status = 'FAIL';
recordError('isolated-appdata-cleanup-sentinel-missing');
}
} catch (error) {
state.status = 'FAIL';
const safeCleanupErrorCode =
@@ -342,8 +379,51 @@ if (selfTestRequested) {
state.isolatedRunner.current?.killHandle,
).catch(() => {});
}
}
for (const session of interactiveCliSessions) {
try {
await waitForInteractiveCliStdioClose(session, 10_000);
} catch (error) {
destroyInteractiveCliOutputStreams(session);
state.status = 'FAIL';
recordError(
error?.code === 'interactive-cli-stdio-close-timeout'
? error.code
: 'interactive-cli-stdio-cleanup-failed',
error,
);
}
}
if (supervisorAutonomousPlayableCliSession) {
state.supervisorAutonomousPlayable.cliOutput = interactiveCliOutput(
supervisorAutonomousPlayableCliSession,
);
}
if (isIsolatedRunnerSuite() && state.isolatedRunner.appDataDir) {
if (state.isolatedRunner.stopped) {
try {
state.isolatedRunner.cleanupPerformed =
await removeIsolatedSuiteAppData();
if (!state.isolatedRunner.cleanupPerformed) {
state.status = 'FAIL';
recordError('isolated-appdata-cleanup-sentinel-missing');
}
} catch (error) {
state.status = 'FAIL';
const safeCleanupErrorCode =
isNonEmptyString(error?.code) &&
/^(?:isolated|source)-[a-z0-9-]+$/u.test(error.code)
? error.code
: 'isolated-appdata-cleanup-failed';
recordError(safeCleanupErrorCode, error);
}
}
const killMethod =
state.isolatedRunner.pidfdClaimCount > 0 ? 'linux-pidfd' : null;
state.isolatedRunner.pidfdClaimCount > 0
? process.platform === 'win32'
? 'windows-process-handle'
: 'linux-pidfd'
: null;
if (isSteerRunnerKillSuite()) {
state.evidence.steerRunnerStopped = state.isolatedRunner.stopped;
state.evidence.steerAppDataCleanupPerformed =
@@ -420,28 +500,6 @@ if (selfTestRequested) {
state.status = 'FAIL';
recordError('web-search-formal-config-cli-call-detected');
}
} else if (isMcpRuntimeSuite()) {
state.evidence.mcpRunnerStopped = state.isolatedRunner.stopped;
state.evidence.mcpAppDataCleanupPerformed =
state.isolatedRunner.cleanupPerformed;
state.evidence.mcpRunnerKillMethod = killMethod;
state.evidence.mcpRunnerPidfdClaimCount =
state.isolatedRunner.pidfdClaimCount;
state.evidence.mcpRunnerPidfdSignalCount =
state.isolatedRunner.pidfdSignalCount;
state.evidence.formalConfigCliCallCount =
state.isolatedRunner.sourceConfigCliCallCount;
state.evidence.sourceRunnerEndpointUnchanged =
state.isolatedRunner.sourceRunnerEndpointUnchanged;
state.evidence.sourceConfigReplicaCount =
state.isolatedRunner.configLinks.length;
state.evidence.sourceConfigReplicasVerified =
state.isolatedRunner.sourceConfigLinksVerified;
state.evidence.isolatedAppDataUsed = true;
if (state.isolatedRunner.sourceConfigCliCallCount > 0) {
state.status = 'FAIL';
recordError('mcp-formal-config-cli-call-detected');
}
} else if (isUserInputRuntimeSuite()) {
state.evidence.userInputRunnerStopped = state.isolatedRunner.stopped;
state.evidence.userInputAppDataCleanupPerformed =
@@ -661,6 +719,61 @@ if (selfTestRequested) {
);
}
}
if (
isSupervisorAutonomousPlayableLaneDefenseSuite() &&
state.isolatedRunner.appDataDir
) {
const snapshot =
state.supervisorAutonomousPlayable.ownedProcessCleanupSnapshot;
if (snapshot) {
try {
const cleanup = await verifyOwnedProcessCleanupSnapshot(snapshot);
state.evidence.ownedProcessIdentityCaptured = true;
state.evidence.ownedRunnerObservedCount =
snapshot.observedCounts.runner;
state.evidence.ownedHelperObservedCount =
snapshot.observedCounts.helper;
state.evidence.ownedNodeDescendantObservedCount =
snapshot.observedCounts.node;
state.evidence.ownedBrowserDescendantObservedCount =
snapshot.observedCounts.browser;
state.evidence.ownedCommandDescendantObservedCount =
snapshot.observedCounts.command;
state.evidence.ownedRunnerResidualCount =
cleanup.residualCounts.runner;
state.evidence.ownedHelperResidualCount =
cleanup.residualCounts.helper;
state.evidence.ownedNodeDescendantResidualCount =
cleanup.residualCounts.node;
state.evidence.ownedBrowserDescendantResidualCount =
cleanup.residualCounts.browser;
state.evidence.ownedCommandDescendantResidualCount =
cleanup.residualCounts.command;
state.evidence.activeCommandChildrenAfterCleanup =
cleanup.activeCommandChildCount;
state.evidence.activeInteractiveCliSessionsAfterCleanup =
cleanup.activeInteractiveCliSessionCount;
state.evidence.ownedProcessCleanupPassed = cleanup.clean;
if (!cleanup.clean) {
state.status = 'FAIL';
recordError(
'supervisor-autonomous-playable-owned-process-residual-detected',
);
}
} catch (error) {
state.status = 'FAIL';
recordError(
'supervisor-autonomous-playable-owned-process-verification-failed',
error,
);
}
} else {
state.status = 'FAIL';
recordError(
'supervisor-autonomous-playable-owned-process-snapshot-missing',
);
}
}
if (
isSteerRunnerKillSuite() &&
state.projectRoot &&
@@ -723,16 +836,6 @@ if (selfTestRequested) {
recordError('context-compaction-partial-evidence-read-failed', error);
}
}
if (isMcpRuntimeSuite() && state.projectRoot && state.status !== 'PASS') {
try {
state.evidence = {
...state.evidence,
...(await collectPartialMcpEvidence()),
};
} catch (error) {
recordError('mcp-partial-evidence-read-failed', error);
}
}
if (
isUserInputRuntimeSuite() &&
state.projectRoot &&
@@ -959,19 +1062,6 @@ if (selfTestRequested) {
report = JSON.stringify(summary, null, 2);
}
}
if (isMcpRuntimeSuite()) {
state.mcp.reportLeakCount = countExactSecrets(
Buffer.from(report),
mcpPrivateValues(),
);
state.evidence.mcpReportLeakCount = state.mcp.reportLeakCount;
if (state.mcp.reportLeakCount > 0) {
state.status = 'FAIL';
recordError('mcp-private-context-report-leak-detected');
summary = buildSummary();
report = JSON.stringify(summary, null, 2);
}
}
if (isUserInputRuntimeSuite()) {
state.userInput.reportLeakCount = countExactSecrets(
Buffer.from(report),
@@ -1074,7 +1164,6 @@ if (selfTestRequested) {
if (
isWebSearchSuite() ||
isContextCompactionSuite() ||
isMcpRuntimeSuite() ||
isUserInputRuntimeSuite() ||
isScopedAgentsSuite() ||
isProjectSkillSuite() ||
@@ -1122,9 +1211,6 @@ if (selfTestRequested) {
const remainingWebSearchReportLeakCount = isWebSearchSuite()
? countExactSecrets(Buffer.from(report), webSearchPrivateLeakValues())
: 0;
const remainingMcpReportLeakCount = isMcpRuntimeSuite()
? countExactSecrets(Buffer.from(report), mcpPrivateValues())
: 0;
const remainingUserInputReportLeakCount = isUserInputRuntimeSuite()
? countExactSecrets(
Buffer.from(report),
@@ -1160,7 +1246,6 @@ if (selfTestRequested) {
const remainingFormalConfigPathReportLeakCount =
isWebSearchSuite() ||
isContextCompactionSuite() ||
isMcpRuntimeSuite() ||
isUserInputRuntimeSuite() ||
isScopedAgentsSuite() ||
isProjectSkillSuite() ||
@@ -1174,7 +1259,6 @@ if (selfTestRequested) {
remainingProjectPathReportLeakCount > 0 ||
remainingResponseStreamReportLeakCount > 0 ||
remainingWebSearchReportLeakCount > 0 ||
remainingMcpReportLeakCount > 0 ||
remainingUserInputReportLeakCount > 0 ||
remainingScopedAgentsReportLeakCount > 0 ||
remainingProjectSkillReportLeakCount > 0 ||
@@ -1189,27 +1273,26 @@ if (selfTestRequested) {
? 'disposable-project-path-report-redaction-required'
: remainingResponseStreamReportLeakCount > 0
? 'response-stream-report-redaction-required'
: remainingMcpReportLeakCount > 0
? 'mcp-report-redaction-required'
: remainingUserInputReportLeakCount > 0
? 'user-input-report-redaction-required'
: remainingScopedAgentsReportLeakCount > 0
? 'scoped-agents-report-redaction-required'
: remainingProjectSkillReportLeakCount > 0
? 'project-skill-report-redaction-required'
: remainingParallelReadReportLeakCount > 0
? 'parallel-read-report-redaction-required'
: remainingSupervisorAutonomousPlayableReportLeakCount > 0
? 'supervisor-autonomous-playable-report-redaction-required'
: remainingSupervisorSwarmReportLeakCount > 0
? 'supervisor-swarm-report-redaction-required'
: remainingFormalConfigPathReportLeakCount > 0
? 'formal-config-path-report-redaction-required'
: 'web-search-report-redaction-required',
: remainingUserInputReportLeakCount > 0
? 'user-input-report-redaction-required'
: remainingScopedAgentsReportLeakCount > 0
? 'scoped-agents-report-redaction-required'
: remainingProjectSkillReportLeakCount > 0
? 'project-skill-report-redaction-required'
: remainingParallelReadReportLeakCount > 0
? 'parallel-read-report-redaction-required'
: remainingSupervisorAutonomousPlayableReportLeakCount > 0
? 'supervisor-autonomous-playable-report-redaction-required'
: remainingSupervisorSwarmReportLeakCount > 0
? 'supervisor-swarm-report-redaction-required'
: remainingFormalConfigPathReportLeakCount > 0
? 'formal-config-path-report-redaction-required'
: 'web-search-report-redaction-required',
);
const safeSummary = {
status: state.status,
suite: state.suite,
providerUsed: false,
blocked: state.blocked,
cleanup: {
performed: state.cleanupPerformed,
@@ -1219,7 +1302,6 @@ if (selfTestRequested) {
projectPathReportLeakCount: remainingProjectPathReportLeakCount,
responseStreamReportLeakCount: remainingResponseStreamReportLeakCount,
webSearchReportLeakCount: remainingWebSearchReportLeakCount,
mcpReportLeakCount: remainingMcpReportLeakCount,
userInputReportLeakCount: remainingUserInputReportLeakCount,
scopedAgentsReportLeakCount: remainingScopedAgentsReportLeakCount,
projectSkillReportLeakCount: remainingProjectSkillReportLeakCount,
File diff suppressed because it is too large Load Diff
@@ -6,7 +6,6 @@ import {
contextCompactionSuite,
goalRuntimeSuite,
localConfigFileName,
mcpRuntimeSuite,
parallelReadSuite,
processSessionSuites,
projectSkillSuite,
@@ -59,7 +58,6 @@ export function parseArguments(args) {
suite === responseStreamSuite ||
suite === webSearchSuite ||
suite === contextCompactionSuite ||
suite === mcpRuntimeSuite ||
suite === userInputRuntimeSuite ||
suite === scopedAgentsSuite ||
suite === projectSkillSuite ||
@@ -19,6 +19,8 @@ import {
} from '../suites/supervisor-swarm.mjs';
import { isIsolatedRunnerSuite } from './reporting.mjs';
export const activeInteractiveCliSessions = new Set();
export async function prepareCliBinary() {
const cargo = process.platform === 'win32' ? 'cargo.exe' : 'cargo';
await runProcess(
@@ -154,36 +156,70 @@ export function startInteractiveCli(args) {
stdio: ['pipe', 'pipe', 'pipe'],
},
);
return createInteractiveCliSession(child);
}
export function createInteractiveCliSession(child) {
activeCommandChildren.add(child);
const session = {
child,
stdout: Buffer.alloc(0),
stderr: Buffer.alloc(0),
exited: false,
exitInfo: null,
exitPromise: null,
closed: false,
closeInfo: null,
closePromise: null,
stdioClosed: false,
stdioCloseInfo: null,
spawnError: null,
stdinError: null,
};
activeInteractiveCliSessions.add(session);
session.exitPromise = new Promise((resolve) => {
const settle = (result) => {
if (session.exited) return;
activeCommandChildren.delete(child);
session.exited = true;
session.closed = true;
session.exitInfo = result;
session.closeInfo = result;
resolve(result);
};
child.once('error', (error) => {
session.spawnError = error;
settle({ code: null, signal: null, error });
});
child.once('exit', (code, signal) => {
settle({ code, signal, error: null });
});
});
session.closePromise = new Promise((resolve) => {
child.on('error', (error) => {
child.once('close', (code, signal) => {
activeCommandChildren.delete(child);
session.closed = true;
session.closeInfo = { code: null, signal: null, error };
resolve(session.closeInfo);
});
child.on('close', (code, signal) => {
activeCommandChildren.delete(child);
session.closed = true;
session.closeInfo = { code, signal, error: null };
resolve(session.closeInfo);
activeInteractiveCliSessions.delete(session);
session.stdioClosed = true;
session.stdioCloseInfo = {
code,
signal,
error: session.spawnError,
};
resolve(session.stdioCloseInfo);
});
});
child.stdin?.on('error', (error) => {
session.stdinError ??= error;
});
child.stdout.on('data', (chunk) => {
state.transcriptScanner?.scan('interactive-stdout', chunk);
state.projectPathTranscriptScanner?.scan('interactive-stdout', chunk);
state.formalConfigPathTranscriptScanner?.scan('interactive-stdout', chunk);
session.stdout = appendBounded(session.stdout, chunk, commandOutputLimit);
});
child.stderr.on('data', (chunk) => {
state.transcriptScanner?.scan('interactive-stderr', chunk);
state.projectPathTranscriptScanner?.scan('interactive-stderr', chunk);
state.formalConfigPathTranscriptScanner?.scan('interactive-stderr', chunk);
session.stderr = appendBounded(session.stderr, chunk, commandOutputLimit);
});
@@ -205,16 +241,23 @@ export async function waitForInteractiveCliOutput(
predicate,
code,
timeoutMs,
{ allowAfterProcessExit = false } = {},
) {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
const output = interactiveCliOutput(session);
if (predicate(output)) return output;
if (session.closed) {
if (session.exited && !allowAfterProcessExit) {
if (session === state.supervisorSwarmCliSession) {
recordSupervisorSwarmChatSessionFailureDiagnostic(session);
}
throw codedError(`${code}-cli-closed`);
throw codedError(`${code}-cli-exited`);
}
if (session.stdioClosed) {
if (session === state.supervisorSwarmCliSession) {
recordSupervisorSwarmChatSessionFailureDiagnostic(session);
}
throw codedError(`${code}-cli-stdio-closed`);
}
await sleep(50);
}
@@ -223,7 +266,7 @@ export async function waitForInteractiveCliOutput(
export async function waitForInteractiveCliExit(session, timeoutMs) {
const result = await Promise.race([
session.closePromise,
session.exitPromise,
sleep(timeoutMs).then(() => null),
]);
if (!result) throw codedError('interactive-cli-exit-timeout');
@@ -236,26 +279,52 @@ export async function waitForInteractiveCliExit(session, timeoutMs) {
}
export async function closeInteractiveCli(session) {
if (!session || session.closed) return;
if (session.child.stdin.writable) {
if (!session) return null;
if (session.exited) return session.exitInfo;
if (
session.child.stdin.writable &&
!session.child.stdin.writableEnded &&
!session.child.stdin.destroyed
) {
session.child.stdin.write('/quit\n');
}
let result = await Promise.race([
session.closePromise,
session.exitPromise,
sleep(3_000).then(() => null),
]);
if (!result && !session.closed) {
if (!result && !session.exited) {
session.child.kill('SIGTERM');
result = await Promise.race([
session.closePromise,
session.exitPromise,
sleep(2_000).then(() => null),
]);
}
if (!result && !session.closed) {
if (!result && !session.exited) {
session.child.kill('SIGKILL');
result = await session.closePromise;
result = await Promise.race([
session.exitPromise,
sleep(5_000).then(() => null),
]);
}
assert(Boolean(result), 'interactive-cli-cleanup-timeout');
return result;
}
export async function waitForInteractiveCliStdioClose(session, timeoutMs) {
if (!session || session.stdioClosed) return session?.stdioCloseInfo ?? null;
const result = await Promise.race([
session.closePromise,
sleep(timeoutMs).then(() => null),
]);
if (!result) throw codedError('interactive-cli-stdio-close-timeout');
return result;
}
export function destroyInteractiveCliOutputStreams(session) {
if (!session) return;
for (const stream of [session.child.stdout, session.child.stderr]) {
if (stream && !stream.destroyed) stream.destroy();
}
}
export async function runProcess(
@@ -329,6 +398,214 @@ export async function runProcess(
});
}
export async function listSystemProcessIdentities() {
if (process.platform === 'win32') {
const systemRoot = process.env.SystemRoot ?? process.env.SYSTEMROOT;
assert(
typeof systemRoot === 'string' && path.isAbsolute(systemRoot),
'owned-process-snapshot-system-root-invalid',
);
const powershell = path.join(
systemRoot,
'System32/WindowsPowerShell/v1.0/powershell.exe',
);
const metadata = await fs.lstat(powershell);
assert(
metadata.isFile() && !metadata.isSymbolicLink(),
'owned-process-snapshot-powershell-invalid',
);
const result = await runProcess(
powershell,
[
'-NoProfile',
'-NonInteractive',
'-Command',
'$processes = @(Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,CreationDate,Name); $processes | ConvertTo-Json -Compress',
],
{
cwd: appRoot,
timeoutMs: 30_000,
env: { ...process.env, NO_COLOR: '1', RUST_BACKTRACE: '0' },
},
);
const parsed = JSON.parse(result.stdout);
return (Array.isArray(parsed) ? parsed : [parsed])
.map((record) => ({
pid: Number(record?.ProcessId),
parentPid: Number(record?.ParentProcessId),
startedAt: String(record?.CreationDate ?? ''),
name: String(record?.Name ?? ''),
}))
.filter(validSystemProcessIdentity);
}
assert(
process.platform === 'linux' || process.platform === 'darwin',
'owned-process-snapshot-platform-unsupported',
);
const result = await runProcess(
'ps',
['-A', '-o', 'pid=', '-o', 'ppid=', '-o', 'lstart=', '-o', 'comm='],
{ cwd: appRoot, timeoutMs: 30_000 },
);
return result.stdout
.split(/\r?\n/u)
.map((line) => line.trim())
.filter(Boolean)
.map((line) => {
const fields = line.split(/\s+/u);
return {
pid: Number(fields[0]),
parentPid: Number(fields[1]),
startedAt: fields.slice(2, 7).join(' '),
name: fields.slice(7).join(' '),
};
})
.filter(validSystemProcessIdentity);
}
function validSystemProcessIdentity(record) {
return (
Number.isSafeInteger(record?.pid) &&
record.pid > 0 &&
Number.isSafeInteger(record.parentPid) &&
record.parentPid >= 0 &&
typeof record.startedAt === 'string' &&
record.startedAt.length > 0 &&
typeof record.name === 'string' &&
record.name.length > 0
);
}
export function buildOwnedProcessCleanupSnapshot(
processRecords,
{ rootPids = [], runnerPid = null, helperPids = [] } = {},
) {
assert(
Array.isArray(processRecords) &&
Array.isArray(rootPids) &&
Array.isArray(helperPids),
'owned-process-snapshot-input-invalid',
);
const records = processRecords.filter(validSystemProcessIdentity);
const byPid = new Map(records.map((record) => [record.pid, record]));
const childrenByParent = new Map();
for (const record of records) {
const children = childrenByParent.get(record.parentPid) ?? [];
children.push(record.pid);
childrenByParent.set(record.parentPid, children);
}
const normalizedRunnerPid = Number.isSafeInteger(runnerPid)
? runnerPid
: null;
const helperPidSet = new Set(
helperPids.filter((pid) => Number.isSafeInteger(pid) && pid > 0),
);
const roots = [
...new Set(
[...rootPids, normalizedRunnerPid, ...helperPidSet].filter(
(pid) => Number.isSafeInteger(pid) && pid > 0,
),
),
];
assert(roots.length > 0, 'owned-process-snapshot-root-missing');
const ownedPids = new Set();
const queue = [...roots];
while (queue.length > 0) {
const pid = queue.shift();
if (ownedPids.has(pid)) continue;
ownedPids.add(pid);
queue.push(...(childrenByParent.get(pid) ?? []));
}
const identities = [...ownedPids]
.map((pid) => byPid.get(pid))
.filter(Boolean)
.map((record) => ({
pid: record.pid,
startedAt: record.startedAt,
name: record.name,
kind: ownedProcessKind(record, normalizedRunnerPid, helperPidSet),
}))
.sort((left, right) => left.pid - right.pid);
return {
identities,
observedCounts: countOwnedProcessKinds(identities),
};
}
function ownedProcessKind(record, runnerPid, helperPids) {
if (record.pid === runnerPid) return 'runner';
if (helperPids.has(record.pid)) return 'helper';
const name = path.basename(record.name).toLowerCase();
if (/^node(?:\.exe)?$/u.test(name)) return 'node';
if (/^(?:chrome|chromium|msedge|google-chrome)(?:\.exe)?$/u.test(name)) {
return 'browser';
}
return 'command';
}
function countOwnedProcessKinds(identities) {
const counts = {
runner: 0,
helper: 0,
node: 0,
browser: 0,
command: 0,
total: identities.length,
};
for (const identity of identities) counts[identity.kind] += 1;
return counts;
}
export function inspectOwnedProcessCleanupResiduals(
snapshot,
processRecords,
{ activeCommandChildCount = 0, activeInteractiveCliSessionCount = 0 } = {},
) {
assert(
Array.isArray(snapshot?.identities) && Array.isArray(processRecords),
'owned-process-residual-input-invalid',
);
const currentByPid = new Map(
processRecords
.filter(validSystemProcessIdentity)
.map((record) => [record.pid, record]),
);
const residualIdentities = snapshot.identities.filter((identity) => {
const current = currentByPid.get(identity.pid);
return (
current?.startedAt === identity.startedAt &&
current?.name === identity.name
);
});
return {
residualCounts: countOwnedProcessKinds(residualIdentities),
activeCommandChildCount,
activeInteractiveCliSessionCount,
clean:
residualIdentities.length === 0 &&
activeCommandChildCount === 0 &&
activeInteractiveCliSessionCount === 0,
};
}
export async function captureOwnedProcessCleanupSnapshot(options) {
return buildOwnedProcessCleanupSnapshot(
await listSystemProcessIdentities(),
options,
);
}
export async function verifyOwnedProcessCleanupSnapshot(snapshot) {
return inspectOwnedProcessCleanupResiduals(
snapshot,
await listSystemProcessIdentities(),
{
activeCommandChildCount: activeCommandChildren.size,
activeInteractiveCliSessionCount: activeInteractiveCliSessions.size,
},
);
}
export function appendBounded(current, chunk, limit) {
const combined = Buffer.concat([current, chunk]);
return combined.length <= limit

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