Compare commits

..

103 Commits

Author SHA1 Message Date
k88936 f7edc9d0ee 区分 Raw 图片 PNG 资源限制错误
Project CI / Frontend tests (pull_request) Successful in 4m43s
Project CI / Repository checks (pull_request) Failing after 11s
Project CI / Backend tests (pull_request) Failing after 10s
Project CI / Native shell tests (pull_request) Failing after 5m14s
将有效但超出解码限制的 PNG 映射为明确的 400 提示

补充超大合法 PNG 的回归测试
2026-09-09 11:04:44 +08:00
k88936 f60771dd00 提前检查 Raw 图片请求预算
在构造 multipart 表单前校验请求 deadline

避免预算耗尽时复制输入图片并缩短耗时统计范围
2026-09-09 10:58:48 +08:00
k88936 f7cccd8592 补充 Raw 图片客户端错误上下文
为 HTTP 客户端构造失败统一保留操作上下文

补充配置错误文案回归测试
2026-09-09 10:57:04 +08:00
k88936 5ee2d940c1 修正 Raw 图片尺寸校验错误语义
尺寸校验失败改用请求参数无效语义

补充错误文案回归测试
2026-09-09 10:55:36 +08:00
k88936 c75a7725ce Merge remote-tracking branch 'origin/master' into feat/expose-image2edit
Project CI / Repository checks (pull_request) Successful in 2m51s
Project CI / Native shell tests (pull_request) Failing after 4m9s
Project CI / Backend tests (pull_request) Failing after 4m58s
Project CI / Frontend tests (pull_request) Successful in 3m46s
2026-09-09 10:42:22 +08:00
k88936 3a95715990 同步 Raw 图片编辑技术方案
Project CI / Native shell tests (pull_request) Failing after 6m36s
Project CI / Backend tests (pull_request) Failing after 14s
Project CI / Repository checks (pull_request) Failing after 15s
Project CI / Frontend tests (pull_request) Successful in 4m22s
记录解码资源上限和阻塞线程策略
补充输出格式回退与成功追踪约定
2026-09-08 23:41:27 +08:00
k88936 ed86963de2 修正 Raw 图片追踪结果类型
明确输出数据集合类型
保持 API 编译检查通过
2026-09-08 23:38:25 +08:00
k88936 65e73eee02 补充 Raw 图片生成成功追踪
记录成功任务与输出数量
完善上游响应和失败阶段日志
2026-09-08 23:25:47 +08:00
k88936 6068eeda83 补充 Raw 图片编辑请求日志
记录上游响应状态与耗时
记录传输失败阶段和结构化上下文
2026-09-08 23:22:56 +08:00
k88936 0e244c64ea 统一 GPT Image 2 尺寸常量
集中维护像素边界和对齐规则
供生成与编辑路径共同引用
2026-09-08 22:11:35 +08:00
suzmii a0e6a83691 修复 AGC 模型选择入口、交互与默认模型回退 (#299)
Project CI / Backend tests (push) Failing after 7m10s
Project CI / Repository checks (push) Successful in 2m19s
Project CI / Frontend tests (push) Successful in 3m32s
Project CI / Native shell tests (push) Failing after 19m48s
### 背景

AGC 后台已支持维护 LLM 模型目录(`agc_model_catalog`),客户端此前只在项目右侧对话浮出模型选择,且存在几个体验问题:

1. 首页聊天框架缺少模型选择入口;
2. 首次进入项目时,右侧模型选择器「点不动」(被 disabled);
3. 模型选择下拉失去焦点不自动收起;
4. 默认模型缺少可视标识;
5. 已保存的选择被后台停用/移除后,客户端显示「请选择模型」,未自动回退默认模型;首页因懒加载在打开前也一直显示「选择模型」。

### 改动内容(完整变更集)

- **首页聊天框架新增模型选择入口**,复用项目右侧对话的 `ConversationModelSelect`;上传按钮在左,模型选择器与发送按钮在右(同一组,模型选择器紧挨发送按钮)。
- **修复首次进入项目时右侧模型选择器点不动**:选择器不再因目录加载(`busy`)或对话进行中(`controlBusy`)被禁用;切换模型只写回客户端配置并作用于后续轮次,发送仍由 `controlBusy` / `modelReady` 把关。
- **模型下拉支持点击外部 / Esc 自动收起**:给组件容器挂 ref,监听 `document` 的 `mousedown` 与 `keydown`,点击容器外或按 Esc 即收起(仅在展开时挂监听)。
- **默认模型增加「默认」标识**:组件记录 `defaultModelId`,在下拉选项中给默认模型显示小标签;亮暗主题自适应。
- **模型选择始终回退默认模型**:已保存选择仍可用时沿用;被停用/移除或从未选择时回退到后台默认模型并写回配置;只有默认模型本身不可用才走错误分流。首页由「懒加载」改为「挂载即加载」,触发按钮直接落在默认模型上,不再出现「选择模型」空态。
- **清理**:移除组件中已无调用方的 `lazy` 加载路径。
- **文档**:同步更新 `【技术方案】AGC后台模型别名与对话选择-2026-09-05.md`(首页入口、对话进行中可切换模型、失焦收起等行为)。

### 关键交互/行为说明

- 触发按钮永远显示模型名(默认或当前选择),不再出现「选择模型」占位,除非默认模型不可用。
- 模型下拉的加载中、刷新、选中态沿用原有交互;默认模型有「默认」标签,便于识别。
- 首页模型选择器不改动「开启创作」门控:目录不可用时仍可创建项目并走后台默认项。

### 验证

- `npm run test -- apps/ai-game-creator-shell/tests/conversationModelSelect.test.tsx` -> 7/7 通过
- `npm run test -- apps/ai-game-creator-shell/tests/appSurface.test.ts` -> 386/386 通过
- `npm run ai-game-creator-shell:typecheck` 通过
- `npm run check:encoding`(4309 文件)通过
- `git diff --check` 通过

### 影响范围与注意事项

- 变更集中在 AGC 客户端(`apps/ai-game-creator-shell`),不涉及 SpacetimeDB schema / 后端接口 / 契约。
- `GET /api/llm/models` 语义不变(仍只返回启用项 + 默认项),本次不改后端。
- 模型下拉在窄屏的换行/遮挡,以及「默认」标签对比度,建议在真机确认一下观感(单测只覆盖逻辑)。

### 关联

- 无关联 Issue;本 PR 独立提交。

---------

Co-authored-by: 段舒康 <kdletters@qq.com>
Reviewed-on: #299
Co-authored-by: 董羽秦 <suzmii@qq.com>
Co-committed-by: 董羽秦 <suzmii@qq.com>
2026-09-08 22:05:15 +08:00
k88936 d3f5d0a355 feat/AGC codex的工具调用 持久化处理 (#282)
Project CI / Repository checks (push) Successful in 2m7s
Project CI / Frontend tests (push) Successful in 2m46s
Project CI / Backend tests (push) Failing after 3m53s
Project CI / Native shell tests (push) Failing after 3m50s
重构directproject的thread持久化,  保存response items用来恢复thread

修改了DirectProject的对话jsonl格式,  不兼容不迁移. (测试需要手动清理旧对话jsonl文件)

AGC显式写入user msg, 对codex 返回的user msg忽略
清理原来的上下文滑动窗口, 因为codex内部会自动compact

工具调用后重启,继续对话:
![shotmd-1788579616.jpg](/attachments/8350c924-9498-47cd-b3da-8be6b79696f9)

close #249
close #277

---------

Co-authored-by: 段舒康 <kdletters@qq.com>
Reviewed-on: #282
Co-authored-by: 王德宇 <kvtodev@outlook.com>
Co-committed-by: 王德宇 <kvtodev@outlook.com>
2026-09-08 22:04:37 +08:00
k88936 01e057d047 Feat/AGC对话渲染markdown (#281)
Project CI / Frontend tests (push) Successful in 3m37s
Project CI / Backend tests (push) Failing after 4m13s
Project CI / Native shell tests (push) Failing after 4m11s
Project CI / Repository checks (push) Successful in 3m24s
before:
 ![image.png](/attachments/ca6f46f7-dcbd-4132-bad2-cf5c83c49096)

after:
 ![shotmd-1788500615.jpg](/attachments/719539a1-9dfb-412a-a9b9-298547b1791c)
![shotmd-1788500598.jpg](/attachments/4656815f-caca-4325-bca3-b29fe5742b6e)

考虑到在AGC场景下不是很有理由, 暂不实现链接, 图片, 内嵌html

---------

Co-authored-by: 段舒康 <kdletters@qq.com>
Reviewed-on: #281
Co-authored-by: 王德宇 <kvtodev@outlook.com>
Co-committed-by: 王德宇 <kvtodev@outlook.com>
2026-09-08 22:03:37 +08:00
kdletters 1f904d28e9 AGC 客户端 MCP 能力暴露 (#274)
Project CI / Frontend tests (push) Successful in 4m2s
Project CI / Repository checks (push) Successful in 2m16s
Project CI / Backend tests (push) Successful in 10m14s
Project CI / Native shell tests (push) Failing after 19m54s
## 目标
保留现有客户端对话与 Codex app-server 链路,把客户端自身受控业务能力通过 MCP 暴露给 Codex。

## 范围
- 客户端会话、项目文件、资源、画布、生成、预览等稳定能力
- 审核 Skill 的索引与按需指导资源
- 复用现有账号、项目路径、权限、计费、幂等、锁和恢复边界

## 明确不做
- 不替换客户端对话入口或 Codex app-server
- 不让客户端替 Codex 判断高层意图、完成状态或规划
- 不暴露任意 Tauri command、shell、凭据、内部 URL、数据库和管理能力

当前 PR 先建立独立分支与审查边界,后续提交实现与定向验证。

Reviewed-on: #274
2026-09-08 22:01:29 +08:00
k88936 ad7c059603 遵守 Raw 图片请求截止时间
发送前裁剪有效超时预算
预算耗尽时提前返回结构化错误
2026-09-08 21:54:22 +08:00
k88936 55d2a393d1 完善 Raw 图片尺寸错误类型
为公开错误实现 Error trait
支持通用错误链处理
2026-09-08 21:52:15 +08:00
k88936 e7f66ce950 修正 Raw 图片传输失败阶段标记
区分请求发送和响应读取失败
让错误详情准确反映故障阶段
2026-09-08 20:03:13 +08:00
k88936 35dcfea3ef 修正 Raw 图片输出格式回退
忽略空值后再使用载荷级格式
补充空字符串和 null 回归测试
2026-09-08 19:56:41 +08:00
k88936 cea8bf77fa 减少 Raw 图片编辑请求拷贝
移动 multipart 选项字段而非克隆
降低掩码和字符串的峰值内存
2026-09-08 19:52:13 +08:00
k88936 0136bfe564 移出 Raw 图片同步解码
将请求预校验放入阻塞线程
避免占用 Tokio 异步工作线程
2026-09-08 19:48:58 +08:00
k88936 22785b4d7b 限制 Raw 图片解码资源
为 PNG 解码设置边长和分配上限
避免压缩图片膨胀导致内存耗尽
2026-09-08 19:45:02 +08:00
k88936 a86a2ee4d2 简化 Raw 图片编辑响应类型
移除无必要的 JSON Value 转换
保持响应结构由类型系统校验
2026-09-08 19:32:55 +08:00
k88936 b22eae2726 收紧 Raw 图片编辑输入契约
Project CI / Frontend tests (pull_request) Successful in 3m51s
Project CI / Native shell tests (pull_request) Successful in 20m20s
Project CI / Repository checks (pull_request) Failing after 14s
Project CI / Backend tests (pull_request) Failing after 14s
新增 3840 边长、16 像素对齐、3:1 比例和总像素范围校验

要求 image 与 mask 为可完整解码的 PNG 并在扣费前拒绝非法输入

将 Raw 路由 JSON body limit 提升至 64 MiB

同步 Raw 图片编辑技术方案和边界测试
2026-09-08 18:18:58 +08:00
k88936 35d0db6377 修复 Raw 图片失败审计与响应格式
Project CI / Frontend tests (pull_request) Successful in 6m7s
Project CI / Native shell tests (pull_request) Successful in 19m41s
Project CI / Repository checks (pull_request) Failing after 11s
Project CI / Backend tests (pull_request) Failing after 9s
为 Raw provider 失败分支补齐结构化 failure audit 并接入 api-server 记录链

使用 VectorEngine 响应 output_format 生成返回图片 MIME 与扩展名

补充响应格式和审计字段定向测试并同步技术方案
2026-09-08 16:19:29 +08:00
kdletters 1498247e9f AGC产物迁移npm与Phaser4
Project CI / Repository checks (push) Successful in 2m54s
Project CI / Native shell tests (push) Failing after 16m56s
Project CI / Frontend tests (push) Successful in 3m4s
Project CI / Backend tests (push) Successful in 8m10s
新增 npm + Vite + Phaser 4.2.1 游戏工程脚手架

允许 DirectProject 在项目边界内安装依赖并执行构建

让预览、静态检查、投影和试玩导出统一使用 dist 产物

补充 Phaser Skill、技术方案、项目记忆和定向测试
2026-09-08 15:11:36 +08:00
suzmii 456cc42dd3 优化泥点流水的素材消耗原因展示 (#302)
Project CI / Repository checks (push) Successful in 1m59s
Project CI / Frontend tests (push) Successful in 3m7s
Project CI / Backend tests (push) Successful in 6m13s
Project CI / Native shell tests (push) Successful in 19m24s
Close #296

## 问题

钱包流水目前只把素材类扣费暴露为 `asset_operation_consume`,共享账单组件统一显示“资产操作消耗”,无法说明本次泥点实际用于哪类素材生成。

## 落地方案

- 在既有钱包流水 metadata 中记录服务端确定的 `assetKind`,不修改 SpacetimeDB schema。
- `api-server` 只将内部素材类型白名单映射为用户可见 `reason`,不暴露原始 metadata、资源 ID 或任务 ID。
- 共享钱包流水契约与组件优先展示具体原因;历史、未知或空 metadata 继续回退为“资产操作消耗”。
- 主站、图片画布与 AGC 继续复用同一共享账单组件。

## 当前进度

- [x] 补充权威后端数据契约
- [x] 写入素材操作类型 metadata
- [x] 扩展钱包流水公开 DTO 与安全映射
- [x] 更新共享账单展示与回归测试
- [x] 完成定向验证与边界检查

## 用户可见映射

- 图片、图标图集、美术规范、UI 设计、发布素材:生成美术素材
- 图片修改:编辑美术素材
- UI 素材提取:提取美术素材
- 角色动画:生成角色动画
- 视频:生成视频素材
- 音效:生成音效素材
- 背景音乐:生成背景音乐
- 历史、未知、空 metadata:资产操作消耗

## 验证

- `cargo fmt --all -- --check`
- `cargo test -p api-server profile_wallet_ledger_reason_only_exposes_known_asset_operations`
- `cargo test -p api-server worker_billing_context_freezes_charge_and_preserves_job_metadata`
- `cargo test -p shared-contracts profile_wallet_ledger_response_uses_camel_case_fields`
- `npx vitest run packages/shared/src/components/PlatformProfileWalletLedgerModal/index.test.tsx`
- `npm run typecheck`
- `npx eslint packages/shared/src/components/PlatformProfileWalletLedgerModal/model.ts packages/shared/src/components/PlatformProfileWalletLedgerModal/index.test.tsx packages/shared/src/contracts/runtime.ts`
- `npm run check:encoding`
- `npm run check:spacetime-schema`
- `git diff --check`

以上均通过;Rust 仅输出仓库已有 dead-code warnings。

## 未验证边界

- 未执行真实登录、生成并读取新钱包流水的端到端联调。本机 `8082/3101` 已由另一套本地栈占用,为避免当前分支挂接同一数据库并带起 worker 处理现有队列,本轮未启动 `npm run dev:api-server`。
- 四项远端 CI 已全部通过,PR 已转为 Ready for review。

## 不变项

- 不修改历史流水数据。
- 不调整泥点价格、扣费顺序、幂等 ledger、失败退款或余额结算。
- 不修改 SpacetimeDB schema。

---------

Co-authored-by: 段舒康 <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/302
Co-authored-by: Suzumiya <suzmii@qq.com>
Co-committed-by: Suzumiya <suzmii@qq.com>
2026-09-08 14:56:16 +08:00
k88936 f087ba3e2b 隔离 Raw 图片代理实现
Project CI / Repository checks (pull_request) Failing after 14s
Project CI / Backend tests (pull_request) Failing after 14s
Project CI / Frontend tests (pull_request) Successful in 36m40s
Project CI / Native shell tests (pull_request) Successful in 54m1s
移除对现有图片编辑 client 与 curl transport 的复用

将 Raw GPT Image 2 请求、multipart 构造和响应解析收敛到独立文件

保持现有 editor 图片链路代码不变
2026-09-08 13:54:31 +08:00
k88936 7fc1b841be 收口 Raw 图片 base64 校验错误
客户端仅返回稳定的 base64 格式错误提示

补充测试避免泄露解码器内部诊断细节
2026-09-08 12:30:55 +08:00
k88936 a576fdcc37 移除未使用的 multipart 请求包装函数
无选项测试直接调用统一传输入口

避免生产构建保留未使用函数告警
2026-09-08 12:29:59 +08:00
k88936 4dcf38715b 补充图片编辑选项 multipart 测试
覆盖质量、背景、输出格式与 mask 文件字段

断言文件名、MIME 类型和字节内容均正确发送
2026-09-08 12:21:11 +08:00
k88936 d1f9f4e1ef 减少图片编辑 multipart 字节复制
将已克隆的选项按值传入阻塞传输函数

构造 mask 与参考图表单时直接移动字节缓冲
2026-09-08 12:16:00 +08:00
k88936 5836aaec26 简化图片编辑 multipart 请求分支
统一通过带选项的 multipart 传输函数发送请求

移除冗余包装函数调用与未使用导入
2026-09-08 12:10:56 +08:00
k88936 4db6d28603 UI编辑器一批细节优化 (#278)
Project CI / Repository checks (push) Successful in 3m6s
Project CI / Frontend tests (push) Successful in 3m44s
Project CI / Backend tests (push) Successful in 8m16s
Project CI / Native shell tests (push) Successful in 20m48s
* 提示词:完善组件建议,增加容器背景与节点大小一致的实现
* 支持Delete删除节点 ctrl + -缩放
* 步骤结束弹窗提醒
![shotmd-1788599068.jpg](/attachments/99a1e4aa-d778-41bf-9dda-0df703723407)
![shotmd-1788605840.jpg](/attachments/31ab883d-32bc-467b-9308-616b83387a79)

* UI 树快捷删除
![shotmd-1788605655.jpg](/attachments/3d451201-3f68-4eb6-b558-864ecf0cb5a3)

* zoom滑动条样式

before:
![image.png](/attachments/e4a04304-e6eb-4da6-9352-99e093fee42e)
after:
![shotmd-1788606577.jpg](/attachments/6cdc377c-f2a3-4a7e-b78e-772a3fdb13b7)

Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/278
Co-authored-by: 王德宇 <kvtodev@outlook.com>
Co-committed-by: 王德宇 <kvtodev@outlook.com>
2026-09-08 11:56:36 +08:00
k88936 18d60feb04 补充 Raw 图片参数预检查
在计费前校验质量、背景和输出格式

规范化可选参数并保持单图请求契约
2026-09-08 10:40:09 +08:00
k88936 4e1316e272 实现 Raw GPT Image 2 图片编辑代理
新增受 Bearer 保护的 /api/raw/v1/images/edit JSON 路由

按单图 image 与可选 mask 转发 GPT Image 2 参数

固定 model 与 n 并返回仅含 data[].b64_json 的响应

复用钱包计费与退款边界并补充单元测试
2026-09-08 10:36:48 +08:00
suzmii e15e3b455f 修复AGC大上下文请求体限制 (#295)
Project CI / Repository checks (push) Successful in 3m12s
Project CI / Frontend tests (push) Successful in 3m21s
Project CI / Backend tests (push) Successful in 7m42s
Project CI / Native shell tests (push) Successful in 20m14s
Project CI / Frontend tests (pull_request) Successful in 50m43s
Project CI / Repository checks (pull_request) Successful in 20m21s
Project CI / Backend tests (pull_request) Successful in 6m18s
Project CI / Native shell tests (pull_request) Successful in 18m19s
## 背景与问题

AGC(AI 游戏创作智能体 App)Direct Codex 模式在携带图片工具结果等大上下文调用 LLM 时,`/api/llm/responses` 与 `/api/llm/chat/completions` 会命中 Axum 默认 **2 MiB** 请求体上限,直接被 413 拒绝,大上下文任务无法执行;同时 Codex app-server 的 failed turn 未把上游 413 映射为稳定错误分类,前端只能显示笼统的“其他错误”,无法引导用户处理。

## 改动内容

**1. api-server(server-rs)**
- 新增常量 `LLM_REQUEST_MAX_BODY_BYTES = 32 MiB`,作为两个 LLM 代理路由的正式请求体上限;
- 两个路由显式配置 `DefaultBodyLimit::max(32 MiB)`,避免 Axum 默认 2 MiB 提前拒绝;handler 内保留超限检查,超过 32 MiB 仍返回 `413 PAYLOAD_TOO_LARGE`;
- 补充回归测试:>2 MiB 大上下文请求不再命中默认限制;超过 32 MiB 仍返回 413。

**2. AGC 壳(apps/ai-game-creator-shell)**
- `codex_app_server` 错误映射:上游/连接层 HTTP 413、`PAYLOAD_TOO_LARGE`、`provider request too large` 等统一映射为稳定分类 `codex-app-server-error:request-too-large`,不再落入 `other` 或误判为权限/安全策略错误;
- 前端新增用户可见文案“模型请求体过大,请减少参考图或上下文后重试”,并补充单测断言。

**3. 文档**
- 【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md 补充“2026-09-06 AGC LLM 代理请求体合同”:明确 32 MiB 上限、必须显式配置 `DefaultBodyLimit`、413 映射规则与用户文案。

## 验证

- `cargo test -p api-server llm_routes_accept_large_context_bodies_beyond_axum_default` 通过
- `cargo test -p api-server llm_responses_rejects_bodies_above_explicit_limit` 通过
- `cargo test ... ai-game-creator-shell ... codex_app_server_failed_turn_maps_request_too_large_details` 通过
- `npm run test -- apps/ai-game-creator-shell/tests/agentRuntimeModel.test.ts`(27 通过)

全量 workspace 测试与 CI 门禁待推送后由 CI 覆盖。

Reviewed-on: #295
Reviewed-by: 段舒康 <kdletters@qq.com>
Co-authored-by: 董羽秦 <suzmii@qq.com>
Co-committed-by: 董羽秦 <suzmii@qq.com>
2026-09-07 17:05:42 +08:00
k88936 302b4addae 补充 Raw GPT Image 2 代理方案
Project CI / Native shell tests (pull_request) Successful in 55m40s
Project CI / Backend tests (pull_request) Failing after 14s
Project CI / Repository checks (pull_request) Failing after 14s
Project CI / Frontend tests (pull_request) Successful in 23m43s
明确 JSON 请求与 data 数组响应

记录预检查和钱包事务边界

拆分 api-server 与 platform-image 文件职责
2026-09-07 16:33:34 +08:00
k88936 964290c641 优化错误报告 (#286)
Project CI / Native shell tests (push) Successful in 21m3s
Project CI / Repository checks (push) Successful in 2m37s
Project CI / Frontend tests (push) Successful in 2m41s
Project CI / Backend tests (push) Successful in 7m2s
样式

before:
![shotmd-1788605870.jpg](/attachments/0470de00-944f-466e-a339-cf9e6cc0b780)
![shotmd-1788605755.jpg](/attachments/3e7be68e-31c5-41ba-9f8b-6dc98ec08868)

after:
![shotmd-1788607435.jpg](/attachments/165c878e-5c1c-4517-b7d5-74ce97a1a99b)
![shotmd-1788608364.jpg](/attachments/4dd20baf-879c-4aa8-ab51-bcc588d48dbc)

逻辑:
每次打开报告modal会再次查询队列中的错误
<video src="attachments/6d9d9bd7-5b7b-4a05-a7c3-ca64b6fb77f3" title="2026-09-05 19-46-02.mp4" controls></video>

---------

Co-authored-by: 段舒康 <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/286
Reviewed-by: 段舒康 <kdletters@qq.com>
Co-authored-by: 王德宇 <kvtodev@outlook.com>
Co-committed-by: 王德宇 <kvtodev@outlook.com>
2026-09-07 15:17:21 +08:00
lhk229 60fbee2353 修复设置页重新出现连接与工具 (#287)
Project CI / Repository checks (pull_request) Failing after 10s
Project CI / Frontend tests (pull_request) Successful in 3m3s
Project CI / Backend tests (pull_request) Failing after 26s
Project CI / Native shell tests (pull_request) Successful in 20m54s
Project CI / Repository checks (push) Successful in 4m37s
Project CI / Frontend tests (push) Successful in 5m31s
Project CI / Backend tests (push) Successful in 8m38s
Project CI / Native shell tests (push) Successful in 20m0s
移除运行时配置中的连接与工具入口及 External Editor 字段

更新 AppSurface 回归断言,确保普通客户端不展示该入口

Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/287
Reviewed-by: 段舒康 <kdletters@qq.com>
Co-authored-by: Linghong <ink29535@proton.me>
Co-committed-by: Linghong <ink29535@proton.me>
2026-09-06 19:47:33 +08:00
kdletters 2975062c69 Merge pull request 'AGC 官方 LLM Router 账号链路与流式联网输出' (#242) from feat/agc-llm-router-official-chain into master
Project CI / Repository checks (push) Successful in 3m22s
Project CI / Frontend tests (push) Successful in 3m16s
Project CI / Backend tests (push) Successful in 6m29s
Project CI / Native shell tests (push) Successful in 18m25s
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/242
2026-09-06 16:49:13 +08:00
suzmii 6166094a3c 修复AGC配置测试字段
Project CI / Repository checks (pull_request) Successful in 2m46s
Project CI / Frontend tests (pull_request) Successful in 2m55s
Project CI / Backend tests (pull_request) Successful in 7m6s
Project CI / Native shell tests (pull_request) Successful in 18m3s
同步配置测试夹具使用 selected_model_id

移除已删除的 model_profiles 与 selected_model_profile_id 字段
2026-09-06 01:16:48 +08:00
suzmii 736a1b6ac6 接入 LLM Router 累计额度结算
Project CI / Repository checks (pull_request) Successful in 2m36s
Project CI / Frontend tests (pull_request) Successful in 3m15s
Project CI / Backend tests (pull_request) Successful in 7m35s
Project CI / Native shell tests (pull_request) Failing after 7m43s
按 Router used_quota 累计值与首次基线结算泥点
新增原子 checkpoint 事务及 llm_router_consume 钱包流水
同步额度查询校验、前端展示、生成绑定和技术文档
2026-09-06 00:36:31 +08:00
suzmii 78f547d26d Merge remote-tracking branch 'origin/master' into feat/agc-llm-router-official-chain
Project CI / Repository checks (pull_request) Successful in 2m38s
Project CI / Frontend tests (pull_request) Successful in 3m46s
Project CI / Backend tests (pull_request) Successful in 6m35s
Project CI / Native shell tests (pull_request) Failing after 8m14s
# Conflicts:
#	apps/ai-game-creator-shell/tests/appSurface/runtime-settings.suite.ts
2026-09-05 20:08:43 +08:00
suzmii 0b9fb40108 Merge remote-tracking branch 'origin/master' into feat/agc-llm-router-official-chain
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
# Conflicts:
#	apps/ai-game-creator-shell/scripts/check-config.mjs
#	apps/ai-game-creator-shell/src/features/runtime-config/RuntimeConfigDialog.tsx
#	apps/ai-game-creator-shell/tests/appSurface/runtime-settings.suite.ts
2026-09-05 20:03:25 +08:00
kdletters c4f344678f 收窄设置页删除改动
Project CI / Repository checks (push) Successful in 2m36s
Project CI / Frontend tests (push) Successful in 3m4s
Project CI / Backend tests (push) Successful in 8m42s
Project CI / Native shell tests (push) Successful in 20m38s
移除多余的页面回归门禁
清理已删除入口的冗余测试断言
2026-09-05 20:01:02 +08:00
suzmii 34e3f70409 补充服务启动配置校验并优化模型菜单
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
启动时校验 LLM Router 地址、模型和相关密钥配置

将刷新操作移入模型展开菜单并修复菜单样式覆盖

更新启动运维文档与定向测试
2026-09-05 19:48:27 +08:00
suzmii 4f3f0f24ff 接入AGC后台模型目录与对话模型选择
新增后台 AGC 模型目录、别名、启停和默认项管理

客户端设置页恢复原状,对话框右下角按别名选择模型

服务端按稳定模型标识映射并校验实际模型白名单

修复 AGC 配套后端端口漂移、启动等待和 SpacetimeDB 版本检查

补充迁移、文档、启动与模型选择测试
2026-09-05 19:10:49 +08:00
kdletters 524e2f0f4f 删除连接与工具设置页
Project CI / Repository checks (push) Successful in 2m34s
Project CI / Frontend tests (push) Successful in 3m3s
Project CI / Backend tests (push) Successful in 6m53s
Project CI / Native shell tests (push) Successful in 17m53s
移除运行设置中的连接与工具导航及页面内容
更新运行设置测试以校验该入口已删除
同步配置门禁,防止已删除页面重新出现
2026-09-05 17:39:05 +08:00
suzmii 76f0c8e58e Merge branch 'master' into feat/agc-llm-router-official-chain
Project CI / Repository checks (pull_request) Successful in 3m39s
Project CI / Frontend tests (pull_request) Successful in 4m0s
Project CI / Backend tests (pull_request) Successful in 8m3s
Project CI / Native shell tests (pull_request) Successful in 19m58s
2026-09-04 19:07:08 +08:00
k88936 f9e1e5d2a7 Fix/修复ui editor选择逻辑 (#270)
Project CI / Backend tests (push) Successful in 7m35s
Project CI / Repository checks (push) Successful in 3m2s
Project CI / Frontend tests (push) Successful in 3m24s
Project CI / Native shell tests (push) Successful in 18m52s
close #266
当前实现: 按下就重新选择

实际上应该: 松开而且没有发生拖动才重新选择

before:
![shotmd-1788407266-compressed.webp](/attachments/251eccf6-ae02-4ae6-b9d2-4eb1fbf67df3)

after:
![shotmd-1788418953-compressed.webp](/attachments/5c05b506-ae08-4cb1-83c8-8da8e1590a7f)

Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/270
Co-authored-by: 王德宇 <kvtodev@outlook.com>
Co-committed-by: 王德宇 <kvtodev@outlook.com>
2026-09-04 16:30:43 +08:00
suzmii 87c0cbd06d 修复 Provider 代理错误外发 AGC 主站客户端标记
Project CI / Repository checks (pull_request) Successful in 3m13s
Project CI / Frontend tests (pull_request) Successful in 3m34s
Project CI / Backend tests (pull_request) Successful in 7m43s
Project CI / Native shell tests (pull_request) Successful in 19m55s
仅 PlatformSession 的 /api/llm 主站路由携带 x-genarrative-client 标记

通用 Provider 与第三方凭据桥接不再自动附加该标记

补充主站路由代理的标记回归测试
2026-09-04 15:52:47 +08:00
suzmii 13639d9f8c 修复 AGC 运行时配置模型方案测试断言
Project CI / Repository checks (pull_request) Successful in 4m23s
Project CI / Frontend tests (pull_request) Successful in 5m11s
Project CI / Backend tests (pull_request) Successful in 7m25s
Project CI / Native shell tests (pull_request) Failing after 15m15s
将推理档断言迁移到模型方案管理面板的推理档位选择器

补充持久化测试 fixture 的 modelProfiles 与选中方案字段

同步保存回写断言中的模型方案结构
2026-09-04 15:19:01 +08:00
suzmii 020f7d7a7a 修复 Router 凭据并发一致性问题
Project CI / Repository checks (pull_request) Failing after 8m28s
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
移除 Router Key 进程内 TTL 缓存,改为每次读取权威账号状态并即时解密当前密文

Router 账号写入改为同主键原地更新,避免 delete+insert 读取缺口

新增 owner/route 映射冲突校验,防止并发写入插入重复账号行

同步更新后端数据契约文档中的凭据读取语义
2026-09-04 14:08:16 +08:00
suzmii aa47fa833d Merge remote-tracking branch 'origin/master' into feat/agc-llm-router-official-chain 2026-09-04 13:54:16 +08:00
suzmii 4bbef74859 feat: AGC 支持模型方案选择
新增 Router 模型目录接口并返回可用模型列表

客户端配置新增模型方案与当前选中方案字段

设置页新增模型方案卡片和管理弹层,支持方案名称、模型和推理强度

自定义下拉菜单圆角、遮罩层级与窗口边界定位,并隐藏滚动条

AGC 官方路由保留所选模型并携带客户端标记,由服务端校验后转发

补齐配置结构变更后的既有测试初始化字段
2026-09-04 13:52:00 +08:00
lhk229 8d13e868bd AGC 更新提示增加 X 关闭按钮 (#276)
Project CI / Frontend tests (push) Successful in 8m49s
Project CI / Repository checks (push) Successful in 8m50s
Project CI / Backend tests (push) Successful in 10m3s
Project CI / Native shell tests (push) Failing after 20m52s
## 变更内容
- 为 AGC 更新提示增加可访问的 X 关闭按钮。
- 复用现有 dismiss 逻辑,手动关闭后立即移除提示。
- 增加关闭按钮的 hover、禁用和图标样式。

## 验证
- npm --prefix apps/ai-game-creator-shell run typecheck
- npm exec vitest run apps/ai-game-creator-shell/tests/appUpdate.test.ts
- npm run check:encoding
- git diff --check

Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/276
Reviewed-by: 段舒康 <kdletters@qq.com>
Co-authored-by: 孔令弘 <ink29535@proton.me>
Co-committed-by: 孔令弘 <ink29535@proton.me>
2026-09-04 11:24:21 +08:00
suzmii 938c37ca1b 修复 AGC 本地 Provider Smoke 路由
Project CI / Repository checks (pull_request) Successful in 3m54s
Project CI / Frontend tests (pull_request) Successful in 5m1s
Project CI / Backend tests (pull_request) Successful in 8m26s
Project CI / Native shell tests (pull_request) Successful in 18m51s
允许显式 Debug E2E 开关使用 loopback Provider

为 smoke 子进程透传环境并启用 Debug E2E 开关
2026-09-03 23:27:54 +08:00
suzmii d0d3466a56 修复 Direct MCP 路径文案泄露
Project CI / Repository checks (pull_request) Successful in 4m24s
Project CI / Frontend tests (pull_request) Successful in 4m46s
Project CI / Backend tests (pull_request) Successful in 7m44s
Project CI / Native shell tests (pull_request) Failing after 13m40s
平台无关拒绝盘符、绝对路径和上级目录

避免未审核路径出现在 Direct Codex 活动文案中
2026-09-03 21:42:39 +08:00
suzmii 75da77dcb4 Merge remote-tracking branch 'origin/master' into feat/agc-llm-router-official-chain
Project CI / Repository checks (pull_request) Successful in 2m31s
Project CI / Backend tests (pull_request) Successful in 8m6s
Project CI / Frontend tests (pull_request) Successful in 3m57s
Project CI / Native shell tests (pull_request) Has been cancelled
2026-09-03 20:07:51 +08:00
suzmii 703b90938b 合并 master 并保留 Router 账号链路整改
合并 origin/master 最新 UI 编辑器与 AGC 运行时变更

保留 external_api_key 恢复为 master 原始结构

保留 LLM Router 凭据统一存储于 llm_router_account 的整改

解决 App.tsx 与 appSurface 测试冲突并完成定向验证
2026-09-03 20:05:50 +08:00
suzmii cbc73f111c 恢复外部 API Key 表并统一 Router 账号存储
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
恢复 external_api_key 表及客户端绑定为 master 原始结构

将 LLM Router 凭据、状态、撤销和缓存统一收敛到 llm_router_account

移除后台与契约中的 purpose 字段及 Router 专用撤销 procedure

同步更新架构文档并补齐相关测试契约
2026-09-03 19:43:10 +08:00
suzmii 270aa38ccb Merge branch 'master' into feat/agc-llm-router-official-chain
Project CI / Repository checks (pull_request) Successful in 3m32s
Project CI / Frontend tests (pull_request) Successful in 3m40s
Project CI / Backend tests (pull_request) Successful in 7m25s
Project CI / Native shell tests (pull_request) Failing after 13m42s
2026-09-03 19:04:22 +08:00
suzmii 6d41dc2f89 切换 LLM Router 凭据到专用账号表
Project CI / Backend tests (pull_request) Failing after 15s
Project CI / Repository checks (pull_request) Failing after 16s
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
- 将 Router 账号、API Key 元数据和密文统一保存到 llm_router_account

- 增加 Router API Key 的进程内缓存并清理旧 external_api_key 兼容路径

- 更新 SpacetimeDB 绑定、后端架构文档和决策记录
2026-09-03 19:03:40 +08:00
suzmii c12d744d91 合并master并保留双侧最新决策记录
Project CI / Frontend tests (pull_request) Successful in 3m16s
Project CI / Repository checks (pull_request) Successful in 2m37s
Project CI / Backend tests (pull_request) Successful in 7m14s
Project CI / Native shell tests (pull_request) Failing after 21m2s
合入主站 AGC 请求头归属与项目命名链路改动。
保留本分支 Direct 过程卡决策记录。
保留主站登录归属、workspace 边界与 Native shell 决策记录。
2026-09-03 16:04:04 +08:00
suzmii e34d797336 修复 AGC 确定性 E2E 与 master 合并后的运行链路
Project CI / Backend tests (pull_request) Failing after 11s
Project CI / Frontend tests (pull_request) Successful in 2m59s
Project CI / Repository checks (pull_request) Failing after 21s
Project CI / Native shell tests (pull_request) Failing after 16m26s
合并 master 后 isolated AppData 配置改为私有副本,避免 Windows 安全校验拒绝硬链接。
只读 runner 状态命令显式配置 config dir,避免误读为 runner 未启用。
为 debug 构建增加仅 E2E 显式开启的确定性 Provider 路由开关,并补充回归测试。
兼容 Windows 对私有副本权限位的校验差异。
2026-09-03 15:59:19 +08:00
suzmii f86a1320f6 Merge remote-tracking branch 'origin/master' into feat/agc-llm-router-official-chain 2026-09-03 14:41:44 +08:00
suzmii 9ae3aba092 清理已退役工具测试并补充 Router 账号文档
Project CI / Repository checks (pull_request) Failing after 11s
Project CI / Backend tests (pull_request) Failing after 10s
Project CI / Frontend tests (pull_request) Successful in 3m26s
Project CI / Native shell tests (pull_request) Has been cancelled
删除已移除 Skill 引用工具的遗留专用测试

补充 llm_router_account 表结构与用途说明
2026-09-03 14:35:22 +08:00
suzmii 8aaa3f5c7b 修复运行时配置诊断阻塞窗口
Project CI / Repository checks (pull_request) Failing after 15s
Project CI / Backend tests (pull_request) Failing after 15s
Project CI / Native shell tests (pull_request) Failing after 11m21s
Project CI / Frontend tests (pull_request) Successful in 11m46s
将 Codex CLI 与 app-server 探测移入 Tokio 阻塞线程池

保持 GameAgent 运行时配置读写流程不变

增加异步 LLM 配置诊断回归测试并补充技术方案约束
2026-09-03 14:24:40 +08:00
suzmii 75845bd7e9 Merge remote-tracking branch 'origin/feat/agc-llm-router-official-chain' into feat/agc-llm-router-official-chain
Project CI / Repository checks (pull_request) Failing after 9s
Project CI / Frontend tests (pull_request) Successful in 3m2s
Project CI / Backend tests (pull_request) Failing after 12s
Project CI / Native shell tests (pull_request) Failing after 7m30s
2026-09-03 13:42:19 +08:00
suzmii 4f79a275a8 美化 AGC 客户端设置页
收敛账号与角色协作展示文案

增加账号状态检测动画和状态卡片样式

将流式输出与联网检索整理为单列

更新设置页回归测试与配置护栏
2026-09-03 13:39:32 +08:00
suzmii 1c4a7bd221 Merge branch 'master' into feat/agc-llm-router-official-chain
Project CI / Repository checks (pull_request) Successful in 2m49s
Project CI / Frontend tests (pull_request) Successful in 3m36s
Project CI / Backend tests (pull_request) Failing after 2m51s
Project CI / Native shell tests (pull_request) Failing after 6m42s
2026-09-03 11:42:37 +08:00
suzmii ac0f6bb783 Revert "恢复 Router provisioning 固定密钥方案"
Project CI / Repository checks (pull_request) Failing after 13s
Project CI / Backend tests (pull_request) Failing after 9s
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
This reverts commit 9a06945e44.
2026-09-03 11:22:29 +08:00
suzmii 9a06945e44 恢复 Router provisioning 固定密钥方案
Project CI / Repository checks (pull_request) Failing after 11s
Project CI / Frontend tests (pull_request) Successful in 3m4s
Project CI / Backend tests (pull_request) Failing after 9s
Project CI / Native shell tests (pull_request) Failing after 10m8s
恢复 api-server 内置 provisioning secret,保持现有账号派生兼容

移除 provisioning secret 的环境变量和示例配置入口

补充阶段性取舍而非设计缺陷的代码注释

同步 Router 账号链路架构文档
2026-09-03 11:10:32 +08:00
suzmii 6bea5bb2b1 合并 master 并整合 Router 安全修复
Project CI / Native shell tests (pull_request) Failing after 6m32s
Project CI / Repository checks (pull_request) Successful in 3m5s
Project CI / Backend tests (pull_request) Failing after 3m16s
Project CI / Frontend tests (pull_request) Successful in 3m44s
合入 origin/master 最新提交 a465e481e

保留后台错误报告、AGC 受控搜索与 Router 计费安全修复

同步 SpacetimeDB migration、技能文档和配置契约
2026-09-03 10:28:20 +08:00
suzmii 4b3d27e3d3 修复 AGC Router 计费与登录安全问题
绑定 LLM 幂等键与请求指纹,阻止跨请求免扣费

放宽登录对 Router 控制面的依赖并迁移 provisioning secret

加强 AGC 配置文件校验与 SSE UTF-8 处理

同步环境配置、架构文档和回归测试
2026-09-03 10:24:36 +08:00
suzmii beae1dfe5f 过程卡工具文案改为用户语义
- MCP 工具按写入、浏览、读取素材、导入、生成图片等用户动作显示文案
- 写入工具与文件变更统一显示正在写入文件和项目相对路径
- 命令显示具体命令,验证类命令显示正在验证游戏
- 未知工具只显示正在调用工具,不暴露内部工具名和未审核参数
- 工具详情限制路径长度并拒绝绝对路径与上跳路径
- stream=false 时继续保留新的具体工具执行文案
- 补充工具映射、安全边界、命令分类与过程卡心跳回归
- 决策记录同步工具语义文案规则
2026-09-02 19:44:57 +08:00
suzmii 73f5c94de4 合并master并保留双侧决策记录
合并 origin/master 的 UI 编辑器、GDD 审批与前端修复。

保留本分支 LLM Router、Direct 过程卡与私有路径相关实现和决策记录。

解决 decision-log 文档冲突,完整保留双方新增决策条目。
2026-09-02 19:31:12 +08:00
suzmii 5008691975 过程卡思考阶段显示正在思考并保留具体执行命令
Project CI / Native shell tests (pull_request) Failing after 12m28s
Project CI / Repository checks (pull_request) Failing after 9s
Project CI / Frontend tests (pull_request) Successful in 3m31s
Project CI / Backend tests (pull_request) Failing after 9s
- preparing 活动词文案由正在理解需求改为正在思考中
- Codex 计划与推理通知只收敛为 thinking 活动,不再把原始推理正文送入 UI
- 推理期 thinking 活动按 1.2 秒限流,避免重复事件刷屏
- command-exec 等执行细节不再依赖回复流开关,stream=false 也保留具体命令
- 同一活动的心跳事件不再用通用文案覆盖正在执行的具体命令
- 补充 Rust 纯函数单测与 AppSurface 思考态、命令心跳回归
- 决策记录同步 thinking 与执行细节展示规则
2026-09-02 18:24:14 +08:00
suzmii c668964d2f 优化 Direct 流式回复展示
将生成中的累计正文直接显示在助手会话气泡中。

过程卡只保留阶段与执行信息,避免工具事件覆盖正文。

展开详情的滚动条轨道与角落改为透明。

补充流式、失败清理与正式消息接管的界面回归验证。
2026-09-02 17:50:17 +08:00
suzmii 8f7017f6c0 Direct 过程卡改为按回合阶段状态驱动并移除合成打字机
Project CI / Frontend tests (pull_request) Successful in 3m20s
Project CI / Repository checks (pull_request) Failing after 14s
Project CI / Backend tests (pull_request) Failing after 11s
Project CI / Native shell tests (pull_request) Failing after 13m20s
- DirectProject observer 只把真实回复增量标记 streaming,工具中间文本与活动一律 running
- 删除最终回复的合成打字机回放,改由真实事件驱动流式展示
- App 事件投影新增回合状态与 processKey,小字统一补充正在前缀
- 过程卡标题只由回合状态决定,展开状态在同一回合内保持
- 失败与接受态文案改为当前阶段描述,旧直接活动词映射移除
- AppSurface 回归覆盖接受态、展开保持、流式标题与失败态断言
- 决策记录与 Direct 审计账本同步新的状态口径
2026-09-02 16:49:42 +08:00
suzmii bbf9500bea 合并最新主线并保留官方路由锁定
Project CI / Frontend tests (pull_request) Failing after 3m0s
Project CI / Repository checks (pull_request) Failing after 3m1s
Project CI / Backend tests (pull_request) Failing after 3m52s
Project CI / Native shell tests (pull_request) Failing after 5m55s
合并客户端扩展、应用更新、格式化门禁和后台表查询等主线改动。

运行时设置页保留官方账号服务与安全元数据,同时接入主线扩展管理与更新能力。

DirectProject 文档合并主线第三方 MCP 支持与官方路由、受控搜索边界。

修复合并后的后台 API Key 详情 Eye 图标导入。
2026-09-02 15:34:30 +08:00
suzmii a30f03a38c 零泥点用户禁止发起 LLM 对话
Project CI / Backend tests (pull_request) Failing after 12s
Project CI / Repository checks (pull_request) Failing after 12s
Project CI / Frontend tests (pull_request) Failing after 6m53s
Project CI / Native shell tests (pull_request) Failing after 9m17s
Responses 与 Chat 兼容入口在解析 Router 凭据前读取用户钱包余额。

余额为 0 时直接返回 409 MUD_POINTS_INSUFFICIENT,不创建、续期或访问 Router 账号。

余额读取失败时失败关闭并提示稍后重试。

补充零余额前置门禁回归测试与架构决策记录。
2026-09-02 15:22:51 +08:00
suzmii 44e1e03f2e 格式化 AGC Rust 模块
Project CI / Frontend tests (pull_request) Failing after 2m13s
Project CI / Native shell tests (pull_request) Failing after 4m39s
Project CI / Repository checks (pull_request) Failing after 13s
Project CI / Backend tests (pull_request) Failing after 11s
按当前 rustfmt 规则整理 Agent Runtime、配置、预览、会话与相关测试的代码布局。

本提交不改变运行逻辑,保持 AGC Rust 工作区格式门禁可通过。
2026-09-02 14:57:18 +08:00
suzmii b414f2f5f8 完善 Router 共享账号与订阅契约
Router 固定 Token 展示名改为 agc_auto_generate,并同步后台安全查询测试。

文档记录跨独立数据库的稳定账号恢复、default 分组、订阅续期和 best-effort 扣费规则。

调整共享官方 Router 的非生产启动告警,并保留错误 code 构造能力。
2026-09-02 14:56:19 +08:00
suzmii 1c6c499a11 登录返回前同步认证投影
新增 create_auth_session_and_sync,统一密码、手机号和微信登录会话的投影同步时机。

避免登录接口签发的新 access token 在下一次请求被当前或其它节点的 SpacetimeDB 校验拒绝。

补充登录投影同步回归测试。
2026-09-02 14:55:52 +08:00
suzmii e9591c0543 锁定 AGC 官方路由并强化美术素材使用
真实 Debug 和 Release 构建都强制通过 API Server 使用官方 LLM Router,仅 Rust 测试构建保留显式 loopback fixture。

清理配置读取中的旧 Provider 凭据入口,并补充配置锁定回归。

code-prototype 任务提示要求实际读取并渲染四类平台美术切片,防止只引用整图或路径。
2026-09-02 14:55:23 +08:00
suzmii 479094ef89 取消 DirectProject 成功回复脱敏
DirectProject 流式文本和最终回复只移除 thinking 包装,成功正文按用户项目内容原样展示。

删除路径、链接和凭据占位符替换以及回复长度截断逻辑。

同步删除最终回复必须脱敏的文档要求,并更新回归测试验证项目内容保持原样。
2026-09-02 14:43:54 +08:00
suzmii 529a1a0a77 格式化 AGC Agent 既有代码
修正 generation 与 runtime_state 两个文件的 rustfmt 布局,使工作区格式门禁可通过。
2026-09-02 14:27:08 +08:00
suzmii 2e15289264 补齐 AGC 直连活动状态与泥点错误映射
Codex app-server 将文件、命令、验证、搜索等 item/started 事件映射为具体活动标题和安全短详情。

泥点不足错误映射为稳定 409 上游错误并在 DirectProject 显示明确充值引导,不再落到 other。

官方平台会话保持 Provider Proxy 链路,测试专用 API Key/AuthBridge 逻辑仅保留在 cfg(test)。Codex 子进程清理代理环境,确保 loopback SSE 不被系统代理劫持。

补充活动投影、错误映射、流式配置和敏感内容脱敏回归测试。
2026-09-02 14:26:00 +08:00
suzmii 69c11c3285 调整 LLM 泥点后置扣费与执行状态展示
LLM Router 计费改为每开始 10000 token 扣 1 点且至少 1 点。

钱包在同一事务内按可消费余额扣光并记录赠送差额,成功响应不再因余额不足中断。

Responses 流式终态延后到结算后发送,泥点不足映射为稳定错误。

执行状态改为具体工具标题,详情默认单行折叠并增加扫过高亮动画。

补充计费公式、幂等扣费与状态展示回归测试。
2026-09-02 14:17:27 +08:00
suzmii 700d7940a7 修复 Router 分组与账号凭据契约
- 固定 Router 用户为 taonier、Token 为 default 并确保订阅

- 复用本地账号 Key,禁止无管理员令牌绕过订阅校验

- LLM 转发只读取服务端已验证的 Router 凭据并保持请求流式参数
2026-09-02 12:06:31 +08:00
suzmii c0694ceedd Merge remote-tracking branch 'origin/master' into feat/hide-llm-router-and-stream-transport
Project CI / Repository checks (pull_request) Failing after 8s
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
# Conflicts:
#	apps/ai-game-creator-shell/src-tauri/src/config.rs
2026-09-01 12:15:24 +08:00
suzmii c2354f855f 收敛 AGC 官方 LLM Router 账号链路
新增 llm_router_account 表、procedures 与客户端绑定
api-server 改为 New API 管理员流程签发独立 Router 账号
Router 成功后执行幂等后置泥点扣费
生产外锁定非生产 Router 控制面为 loopback
AGC 正式模式清除手工 Provider 凭据并锁定官方代理
AGC 状态面只返回账号凭据与官方路由安全元数据
后台数据库表查询适配账号凭据状态展示
补充 AGC 与后端测试、环境示例和架构文档
修正 dev-stack 状态路径按仓库脚本位置解析
2026-09-01 12:03:07 +08:00
suzmii 26e4eab6b6 对齐主线ACL文件边界
将AGC Rust源码中的ACL专用实现恢复为主线版本

移除本分支内的Windows ACL提权命令与私有路径调用

保留Router、联网搜索和流式传输改动在后续stash中继续处理
2026-08-31 13:09:29 +08:00
suzmii c3c572dbac 合并最新 Agent 编排基线
合并 origin/master 的 Agent Runtime 编排依赖与测试门禁

恢复 platform-agent 的任务图校验与动态编排实现

为后续 AGC Router 流程调试提供可启动的最新基线
2026-08-31 11:54:18 +08:00
suzmii 7132ce543e 修正Responses流式结算事件顺序
延迟透传上游 [DONE],确保扣费失败时客户端先收到 error 事件

补充 SSE done 标记边界解析回归测试
2026-08-30 16:18:19 +08:00
suzmii 9f832ee96a 完善AGC Router代理与后置计费
修正无 provisioning 时仅使用服务端 fallback Key,禁止生成 Router 不认识的随机 Key

补齐 AGC 凭据元数据脱敏、失效复用保护与 SpacetimeDB 字段映射

让 Responses 与 Chat 流式调用在成功后按 usage 幂等扣除泥点

同步 schema 迁移、环境变量示例、后端架构和决策文档
2026-08-30 16:14:10 +08:00
suzmii e2689b4be5 调整配置默认值回归断言
移除已不再存在的前端 Agent 推理映射源码断言

保留 Rust 侧规范 Agent 默认映射和配置模板验证
2026-08-30 14:43:06 +08:00
suzmii ddb668710e 补回联网搜索测试依赖导入
保留 DirectToolBridge 测试路由使用的 Query 和 get
2026-08-30 14:36:41 +08:00
suzmii b27df8dfab 修正配置迁移测试可见性
开放 AGC 配置迁移测试所需的 crate 内函数

清理受控联网搜索合并后的未使用导入
2026-08-30 14:35:24 +08:00
suzmii 8d6b923607 合并AGC流式输出与受控联网搜索
保留官方路由锁定、账号凭据状态和 Windows 私有路径安全边界

合入 DirectProject 流式可见文本投影与受控联网搜索

同步配置 schema 迁移、状态输出和回归测试
2026-08-30 14:32:35 +08:00
suzmii 68dfd2c24f 补齐AGC流式输出与受控联网搜索
增加 DirectProject 流式可见文本投影和敏感内容过滤

接入受控 web search 工具及参数、网络边界测试

补充配置迁移和运行时回归覆盖
2026-08-30 14:22:03 +08:00
suzmii 93477f2b1b 重整AGC官方路由与安全文件链路
记录当前 AGC 客户端、Tauri、API Server、后台查询和契约改动

保留后续按新 Router 后端方案重构的检查点
2026-08-30 14:21:18 +08:00
252 changed files with 23021 additions and 3577 deletions
+5
View File
@@ -8,6 +8,11 @@ LLM_BASE_URL="https://api.vectorengine.cn/v1"
# but it should not be relied on by browser code.
LLM_API_KEY=""
# Router account provisioning secret (server-side only). Prefer the protected
# file form in production; never expose either value to clients or commit it.
GENARRATIVE_LLM_ROUTER_PROVISIONING_SECRET=""
GENARRATIVE_LLM_ROUTER_PROVISIONING_SECRET_FILE=""
# Optional frontend override for the local proxy path.
VITE_LLM_PROXY_BASE_URL="/api/llm"
+50
View File
@@ -26,6 +26,8 @@ import type {
AdminErrorReportDetail,
AdminErrorReportEntry,
AdminErrorReportListResponse,
AdminExternalApiKeyListQuery,
AdminExternalApiKeyListResponse,
AdminFeatureGateConfigResponse,
AdminLoginResponse,
AdminMeResponse,
@@ -249,6 +251,16 @@ export function getAdminDatabaseTableRows(
);
}
export function getAdminExternalApiKeys(
token: string,
query: AdminExternalApiKeyListQuery = {},
) {
return request<AdminExternalApiKeyListResponse>(
`/admin/api/external-api-keys${buildExternalApiKeyQuery(query)}`,
{ token },
);
}
export function debugAdminHttp(token: string, payload: AdminDebugHttpRequest) {
return request<AdminDebugHttpResponse>('/admin/api/debug/http', {
method: 'POST',
@@ -928,6 +940,28 @@ function buildDatabaseTableRowsQuery(query: AdminDatabaseTableRowsQuery) {
return queryString ? `?${queryString}` : '';
}
function buildExternalApiKeyQuery(query: AdminExternalApiKeyListQuery) {
const params = new URLSearchParams();
appendQueryParam(params, 'ownerUserId', query.ownerUserId);
appendQueryParam(params, 'publicUserCode', query.publicUserCode);
appendQueryParam(params, 'keyId', query.keyId);
appendQueryParam(params, 'name', query.name);
appendQueryParam(params, 'keyPrefix', query.keyPrefix);
appendQueryParam(params, 'createdAfter', query.createdAfter);
appendQueryParam(params, 'createdBefore', query.createdBefore);
appendQueryParam(params, 'status', query.status);
if (typeof query.limit === 'number' && Number.isFinite(query.limit)) {
params.set('limit', String(Math.floor(query.limit)));
}
if (typeof query.offset === 'number' && Number.isFinite(query.offset)) {
params.set('offset', String(Math.floor(query.offset)));
}
appendQueryParam(params, 'sortColumn', query.sortColumn);
appendQueryParam(params, 'sortDirection', query.sortDirection);
const queryString = params.toString();
return queryString ? `?${queryString}` : '';
}
function buildEditorAssetListQuery(query: AdminEditorAssetListQuery) {
const params = new URLSearchParams();
appendQueryParam(params, 'cursor', query.cursor);
@@ -1038,3 +1072,19 @@ function buildAdminApiError(
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
export function getAgcModelCatalog(token: string) {
return request<import('./adminApiTypes').AdminAgcModelCatalog>(
'/admin/api/agc-models',
{ token },
);
}
export function saveAgcModelCatalog(
token: string,
body: import('./adminApiTypes').AdminAgcModelCatalog,
) {
return request<import('./adminApiTypes').AdminAgcModelCatalog>(
'/admin/api/agc-models',
{ token, method: 'PUT', body },
);
}
+57
View File
@@ -275,6 +275,51 @@ export interface AdminDatabaseTableStatPayload {
errorMessage: string | null;
}
export interface AdminExternalApiKeyListQuery {
ownerUserId?: string;
publicUserCode?: string;
keyId?: string;
name?: string;
keyPrefix?: string;
createdAfter?: string;
createdBefore?: string;
status?: 'active' | 'revoked';
limit?: number;
offset?: number;
sortColumn?:
| 'keyId'
| 'ownerUserId'
| 'name'
| 'keyPrefix'
| 'createdAt'
| 'lastUsedAt'
| 'updatedAt';
sortDirection?: 'asc' | 'desc';
}
export interface AdminExternalApiKeyPayload {
keyId: string;
ownerUserId: string;
name: string;
keyPrefix: string;
scopes: string[];
createdAt: string;
lastUsedAt: string | null;
revokedAt: string | null;
updatedAt: string;
status: 'active' | 'revoked';
}
export interface AdminExternalApiKeyListResponse {
keys: AdminExternalApiKeyPayload[];
total: number;
limit: number;
offset: number;
scannedCount: number;
scanLimit: number;
scanLimitReached: boolean;
}
export interface AdminDebugHeaderInput {
name: string;
value: string;
@@ -953,3 +998,15 @@ export interface AdminRechargeRefundActionResponse {
export interface AdminWalletRestrictionResponse {
wallet: AdminProfileWalletPayload;
}
export interface AdminAgcModel {
id: string;
alias: string;
modelId: string;
enabled: boolean;
}
export interface AdminAgcModelCatalog {
revision: number;
defaultModelId: string;
models: AdminAgcModel[];
}
+4
View File
@@ -18,6 +18,7 @@ import {
setStoredAdminToken,
} from '../auth/adminAuthStore';
import { AdminAccountsPage } from '../pages/AdminAccountsPage';
import { AdminAgcModelsPage } from '../pages/AdminAgcModelsPage';
import { AdminDashboardPage } from '../pages/AdminDashboardPage';
import { AdminDatabaseTablesPage } from '../pages/AdminDatabaseTablesPage';
import { AdminDebugHttpPage } from '../pages/AdminDebugHttpPage';
@@ -289,6 +290,9 @@ export function AdminApp() {
onUnauthorized={handleUnauthorized}
/>
) : null}
{activeRouteId === 'agc-models' ? (
<AdminAgcModelsPage token={token} onUnauthorized={handleUnauthorized} />
) : null}
{activeRouteId === 'editor-showcase' ? (
<AdminEditorShowcaseReviewPage
token={token}
+1
View File
@@ -50,6 +50,7 @@ const routeIcons = {
'editor-showcase': Star,
'editor-assets': Images,
accounts: Users,
'agc-models': ListChecks,
} satisfies Record<AdminRouteId, typeof LayoutDashboard>;
export function AdminShell({
+6 -1
View File
@@ -16,9 +16,13 @@ export type AdminRouteId =
| 'editor-generation-pricing'
| 'editor-showcase'
| 'editor-assets'
| 'agc-models'
| 'accounts';
export type AdminTabPermission = Exclude<AdminRouteId, 'accounts'>;
export type AdminTabPermission = Exclude<
AdminRouteId,
'accounts' | 'agc-models'
>;
/** 后台导航项定义,hash 是浏览器地址栏和移动底栏共用入口。 */
export interface AdminRouteDefinition {
@@ -47,6 +51,7 @@ export const adminRoutes: AdminRouteDefinition[] = [
label: '模型定价',
hash: '#editor-generation-pricing',
},
{ id: 'agc-models', label: 'AGC 模型', hash: '#agc-models', ownerOnly: true },
{ id: 'editor-showcase', label: '精选审核', hash: '#editor-showcase' },
{ id: 'editor-assets', label: '素材查询', hash: '#editor-assets' },
{ id: 'accounts', label: '账号管理', hash: '#accounts', ownerOnly: true },
@@ -0,0 +1,56 @@
// @vitest-environment jsdom
import {
cleanup,
fireEvent,
render,
screen,
waitFor,
} from '@testing-library/react';
import { afterEach, expect, test, vi } from 'vitest';
import { getAgcModelCatalog, saveAgcModelCatalog } from '../api/adminApiClient';
import { AdminAgcModelsPage } from './AdminAgcModelsPage';
vi.mock('../api/adminApiClient', () => ({
getAgcModelCatalog: vi.fn(),
saveAgcModelCatalog: vi.fn(),
isAdminApiError: vi.fn(() => false),
formatAdminApiError: vi.fn(() => '保存失败'),
}));
vi.mock('../components/useAdminWriteConfirm', () => ({
useAdminWriteConfirm: () => ({
confirmWrite: async () => true,
confirmDialog: null,
}),
}));
afterEach(cleanup);
test('edits alias and upstream model without changing the stable identifier or revision', async () => {
const catalog = {
revision: 3,
defaultModelId: 'quality',
models: [
{ id: 'quality', alias: '高质量', modelId: 'gpt-6-astra', enabled: true },
],
};
vi.mocked(getAgcModelCatalog).mockResolvedValue(catalog);
vi.mocked(saveAgcModelCatalog).mockImplementation(async (_, input) => ({
...input,
revision: 4,
}));
render(<AdminAgcModelsPage token="test" onUnauthorized={vi.fn()} />);
await screen.findByDisplayValue('gpt-6-astra');
fireEvent.change(screen.getByLabelText('模型 1 别名'), {
target: { value: '精细创作' },
});
fireEvent.click(screen.getByRole('button', { name: '保存' }));
await waitFor(() =>
expect(saveAgcModelCatalog).toHaveBeenCalledWith('test', {
...catalog,
models: [{ ...catalog.models[0], alias: '精细创作' }],
}),
);
await waitFor(() => {
expect(screen.getAllByText('已保存').length).toBeGreaterThanOrEqual(2);
});
});
@@ -0,0 +1,255 @@
import { CircleHelp, Plus, RefreshCcw, Save, Trash2 } from 'lucide-react';
import { useEffect, useState } from 'react';
import { getAgcModelCatalog, saveAgcModelCatalog } from '../api/adminApiClient';
import type { AdminAgcModel, AdminAgcModelCatalog } from '../api/adminApiTypes';
import { useAdminWriteConfirm } from '../components/useAdminWriteConfirm';
import { handlePageError } from './pageUtils';
export function AdminAgcModelsPage({
token,
onUnauthorized,
}: {
token: string;
onUnauthorized: (message?: string) => void;
}) {
const [catalog, setCatalog] = useState<AdminAgcModelCatalog | null>(null);
const [busy, setBusy] = useState(false);
const [error, setError] = useState('');
const [saved, setSaved] = useState(false);
const { confirmWrite, confirmDialog } = useAdminWriteConfirm();
async function refresh() {
if (!token) return;
setBusy(true);
setError('');
setSaved(false);
try {
setCatalog(await getAgcModelCatalog(token));
} catch (error) {
handlePageError(error, onUnauthorized, setError);
} finally {
setBusy(false);
}
}
useEffect(() => {
void refresh();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [token]);
function update(id: string, patch: Partial<AdminAgcModel>) {
setSaved(false);
setCatalog(
(current) =>
current && {
...current,
models: current.models.map((model) =>
model.id === id ? { ...model, ...patch } : model,
),
},
);
}
async function save() {
if (!catalog || busy) return;
if (
!(await confirmWrite({
action: '保存 AGC 模型目录',
target: `${catalog.models.length} 个模型`,
}))
)
return;
setBusy(true);
setError('');
setSaved(false);
try {
setCatalog(await saveAgcModelCatalog(token, catalog));
setSaved(true);
} catch (error) {
handlePageError(error, onUnauthorized, setError);
} finally {
setBusy(false);
}
}
return (
<section className="admin-page admin-page-wide admin-agc-models">
<div className="admin-page-heading">
<div>
<h2>AGC </h2>
<p></p>
</div>
<span className="admin-agc-models-revision">
v{catalog?.revision ?? '-'}
</span>
</div>
<div className="admin-agc-models-summary">
<div>
<span></span>
<strong>
{catalog?.models.filter((model) => model.enabled).length ?? 0}
</strong>
</div>
<div>
<span></span>
<strong>
{catalog
? (catalog.models.find(
(model) => model.id === catalog.defaultModelId,
)?.alias ?? '未设置')
: '未设置'}
</strong>
</div>
<div>
<span></span>
<strong>{busy ? '处理中' : saved ? '已保存' : '待修改'}</strong>
</div>
</div>
<section className="admin-panel admin-agc-models-panel">
<div className="admin-panel-heading">
<div>
<h3></h3>
<span></span>
</div>
<CircleHelp size={17} aria-label="模型目录帮助" />
</div>
<div className="admin-agc-models-toolbar">
<button
type="button"
title="重新读取"
aria-label="重新读取模型"
disabled={busy}
onClick={() => void refresh()}
>
<RefreshCcw size={16} />
</button>
<button
type="button"
title="添加模型"
aria-label="添加模型"
disabled={busy || !catalog || catalog.models.length >= 32}
onClick={() => {
setSaved(false);
setCatalog(
(current) =>
current && {
...current,
models: [
...current.models,
{
id: crypto.randomUUID(),
alias: '',
modelId: '',
enabled: true,
},
],
},
);
}}
>
<Plus size={16} />
</button>
<button
type="button"
disabled={busy || !catalog}
onClick={() => void save()}
>
<Save size={16} />
</button>
</div>
{error ? <p role="alert">{error}</p> : null}
{saved ? <p role="status"></p> : null}
{busy ? <p role="status"></p> : null}
<div className="admin-table-wrap admin-agc-models-table">
<table className="admin-table admin-agc-models-table-grid">
<thead>
<tr>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{catalog?.models.map((model, index) => (
<tr key={model.id}>
<td>
<input
aria-label={`模型 ${index + 1} 别名`}
maxLength={40}
required
value={model.alias}
disabled={busy}
onChange={(e) =>
update(model.id, { alias: e.target.value })
}
/>
</td>
<td>
<input
aria-label={`模型 ${index + 1} 实际模型名`}
maxLength={200}
required
value={model.modelId}
disabled={busy}
onChange={(e) =>
update(model.id, { modelId: e.target.value })
}
/>
</td>
<td>
<input
aria-label={`模型 ${index + 1} 启用`}
type="checkbox"
checked={model.enabled}
disabled={busy || model.id === catalog.defaultModelId}
onChange={(e) =>
update(model.id, { enabled: e.target.checked })
}
/>
</td>
<td>
<input
aria-label={`模型 ${index + 1} 默认`}
name="agc-default-model"
type="radio"
checked={model.id === catalog.defaultModelId}
disabled={busy || !model.enabled}
onChange={() => {
setSaved(false);
setCatalog({ ...catalog, defaultModelId: model.id });
}}
/>
</td>
<td>
<button
type="button"
title="删除模型"
aria-label={`删除模型 ${index + 1}`}
disabled={busy || model.id === catalog.defaultModelId}
onClick={() => {
setSaved(false);
setCatalog({
...catalog,
models: catalog.models.filter(
(candidate) => candidate.id !== model.id,
),
});
}}
>
<Trash2 size={16} />
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</section>
{confirmDialog}
</section>
);
}
@@ -7,6 +7,7 @@ import { beforeEach, expect, test, vi } from 'vitest';
import {
getAdminDatabaseTableRows,
getAdminDatabaseTables,
getAdminExternalApiKeys,
} from '../api/adminApiClient';
import type { AdminDatabaseTableRowsResponse } from '../api/adminApiTypes';
import {
@@ -20,6 +21,7 @@ vi.mock('../api/adminApiClient', () => ({
),
getAdminDatabaseTableRows: vi.fn(),
getAdminDatabaseTables: vi.fn(),
getAdminExternalApiKeys: vi.fn(),
isAdminApiError: vi.fn(() => false),
}));
@@ -74,6 +76,7 @@ const referralRows = [
beforeEach(() => {
vi.clearAllMocks();
window.location.hash = '#tables?table=profile_referral_relation';
vi.mocked(getAdminExternalApiKeys).mockReset();
vi.mocked(getAdminDatabaseTables).mockResolvedValue({
fetchErrors: [],
tables: ['profile_referral_relation'],
@@ -92,6 +95,55 @@ beforeEach(() => {
});
});
test('external_api_key 使用专用安全查询且详情不展示原始 JSON', async () => {
const user = userEvent.setup();
window.location.hash = '#tables?table=external_api_key';
vi.mocked(getAdminDatabaseTables).mockResolvedValue({
fetchErrors: [],
tables: ['external_api_key'],
});
vi.mocked(getAdminExternalApiKeys).mockResolvedValue({
keys: [
{
keyId: 'external-api-key-1',
ownerUserId: 'user-1',
name: 'agc_auto_generate',
keyPrefix: 'tnr_sk_fixture',
scopes: ['llm:responses'],
createdAt: '2026-08-29T00:00:00Z',
lastUsedAt: null,
revokedAt: null,
updatedAt: '2026-08-29T00:00:00Z',
status: 'active',
},
],
total: 1,
limit: 100,
offset: 0,
scannedCount: 1,
scanLimit: 5000,
scanLimitReached: false,
});
render(
<AdminDatabaseTablesPage token="admin-token" onUnauthorized={vi.fn()} />,
);
await user.type(screen.getByPlaceholderText('精确 ownerUserId'), 'user-1');
await user.click(screen.getByRole('button', { name: '安全查询' }));
await waitFor(() => {
expect(getAdminExternalApiKeys).toHaveBeenLastCalledWith(
'admin-token',
expect.objectContaining({ ownerUserId: 'user-1' }),
);
});
expect(await screen.findByText('tnr_sk_fixture')).toBeTruthy();
await user.click(screen.getByRole('button', { name: '详情' }));
expect(screen.getByRole('dialog')).toBeTruthy();
expect(screen.queryByText('复制 JSON')).toBeNull();
expect(screen.queryByText('key_hash')).toBeNull();
});
test('后台表查询页通过页面级固定栏翻页并提示扫描结果可能不完整', async () => {
const user = userEvent.setup();
vi.mocked(getAdminDatabaseTableRows).mockResolvedValue({
File diff suppressed because it is too large Load Diff
+137 -2
View File
@@ -3093,5 +3093,140 @@ button:disabled {
background: var(--admin-surface, #fff);
}
.admin-detail-modal__panel header,
.admin-detail-modal__actions { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.admin-detail-modal__panel pre { max-height: 360px; overflow: auto; white-space: pre-wrap; background: #f8fafc; padding: 12px; border-radius: 8px; }
.admin-detail-modal__actions {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.admin-detail-modal__panel pre {
max-height: 360px;
overflow: auto;
white-space: pre-wrap;
background: #f8fafc;
padding: 12px;
border-radius: 8px;
}
.admin-agc-models {
min-width: 0;
}
.admin-agc-models-revision {
padding: 6px 10px;
border: 1px solid #e7d9cc;
border-radius: 999px;
background: #fffaf6;
color: #9a8170;
font-size: 12px;
font-variant-numeric: tabular-nums;
}
.admin-agc-models-summary {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 12px;
}
.admin-agc-models-summary > div {
display: grid;
gap: 6px;
padding: 16px 18px;
border: 1px solid #eadfd6;
border-radius: 10px;
background: #fffdfa;
}
.admin-agc-models-summary span,
.admin-agc-models-panel > .admin-panel-heading span {
color: #9a8170;
font-size: 12px;
}
.admin-agc-models-summary strong {
overflow: hidden;
color: #3d2a20;
font-size: 20px;
text-overflow: ellipsis;
white-space: nowrap;
}
.admin-agc-models-panel {
border: 1px solid #eadfd6;
border-radius: 10px;
background: #fffdfa;
box-shadow: 0 10px 30px rgb(78 48 28 / 6%);
}
.admin-agc-models-panel > .admin-panel-heading > div {
display: grid;
gap: 4px;
}
.admin-agc-models-panel > .admin-panel-heading > svg {
color: #b9947a;
}
.admin-agc-models-toolbar {
display: flex;
justify-content: flex-end;
gap: 8px;
padding: 4px 0 8px;
}
.admin-agc-models-toolbar button,
.admin-agc-models-table-grid button {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
min-height: 34px;
padding: 0 11px;
border: 1px solid #e2d3c7;
border-radius: 8px;
background: #fffaf6;
color: #684d3d;
font-size: 12px;
font-weight: 700;
cursor: pointer;
}
.admin-agc-models-toolbar button:hover,
.admin-agc-models-toolbar button:focus-visible,
.admin-agc-models-table-grid button:hover,
.admin-agc-models-table-grid button:focus-visible {
border-color: #c99d80;
background: #fff;
outline: none;
}
.admin-agc-models-toolbar button:last-child {
border-color: #a96442;
background: #a96442;
color: #fff;
}
.admin-agc-models-table-grid {
min-width: 720px;
}
.admin-agc-models-table-grid th {
padding-top: 12px;
padding-bottom: 12px;
background: #fcf7f2;
}
.admin-agc-models-table-grid td {
padding-top: 14px;
padding-bottom: 14px;
}
.admin-agc-models-table-grid td input:not([type]) {
width: 100%;
min-width: 180px;
box-sizing: border-box;
padding: 9px 10px;
border: 1px solid #e1d3c8;
border-radius: 7px;
background: #fff;
color: #3d2a20;
}
.admin-agc-models-table-grid td input:not([type]):focus-visible {
border-color: #b97854;
outline: none;
box-shadow: 0 0 0 3px rgb(185 120 84 / 14%);
}
.admin-agc-models-table-grid td:has(input[type='checkbox']),
.admin-agc-models-table-grid td:has(input[type='radio']) {
width: 72px;
text-align: center;
vertical-align: middle;
}
@media (max-width: 680px) {
.admin-agc-models-summary {
grid-template-columns: 1fr;
}
}
@@ -1,13 +1,14 @@
{
"schemaVersion": "game-creator-config.v2",
"agentMode": "codex_app_server",
"llm": {
"apiKey": "",
"baseUrl": "https://dev.genarrative.world/gpt/v1",
"model": "gpt-5.6-sol",
"model": "gpt-6-astra",
"apiKind": "openai_responses",
"reasoningEffort": "max",
"stream": true,
"webSearchEnabled": false,
"webSearchEnabled": true,
"contextWindowTokens": 128000,
"autoCompactTokenLimit": 64000,
"toolOutputTokenLimit": 12000,
+1
View File
@@ -53,6 +53,7 @@
"focus-trap-react": "^12.0.3",
"lexical": "^0.47.0",
"lucide-react": "^0.546.0",
"phaser": "^4.2.1",
"react": "^19.0.0",
"react-arborist": "^3.16.0",
"react-colorful": "^5.8.0",
@@ -1948,6 +1948,7 @@ async function runE2e(options) {
...process.env,
NO_COLOR: '1',
[platformSessionFixtureEnv]: fixturePath,
GENARRATIVE_AGC_DEBUG_PROVIDER_E2E: '1',
}),
);
childReport = parseChildReport(childResult);
@@ -773,7 +773,8 @@ export async function prepareIsolatedSuiteAppData({
isScopedAgentsSuite() ||
isProjectSkillSuite() ||
isParallelReadSuite() ||
isSupervisorSwarmSuite()
isSupervisorSwarmSuite() ||
isSupervisorAutonomousPlayableLaneDefenseSuite()
? 'private-copy'
: 'hardlink';
try {
@@ -796,7 +797,7 @@ export async function prepareIsolatedSuiteAppData({
storageMode === 'private-copy' &&
(linkedMetadata.dev !== source.metadata.dev ||
linkedMetadata.ino !== source.metadata.ino) &&
(linkedMetadata.mode & 0o077) === 0;
(process.platform === 'win32' || (linkedMetadata.mode & 0o077) === 0);
const hardlinkValid =
storageMode === 'hardlink' &&
linkedMetadata.dev === source.metadata.dev &&
@@ -1976,7 +1977,7 @@ export async function verifyIsolatedSuiteConfigLinksUnchanged() {
linkedMetadata.ino === link.linkedIno &&
(linkedMetadata.dev !== sourceMetadata.dev ||
linkedMetadata.ino !== sourceMetadata.ino) &&
(linkedMetadata.mode & 0o077) === 0
(process.platform === 'win32' || (linkedMetadata.mode & 0o077) === 0)
: linkedMetadata.dev === link.dev && linkedMetadata.ino === link.ino;
const sourceMetadataStable =
sourceMetadata.mode === link.sourceMode &&
@@ -107,12 +107,16 @@ const rustSharedContractSource = fs.readFileSync(
'utf8',
);
const allowedUncalledTauriCommands = [
'append_direct_project_conversation_message',
'chat_with_game_creator_agent',
'check_ui_editor_font_glyph_coverage',
'create_ui_design_resource',
'open_game_creator_launcher_window',
'open_game_creator_workspace_window',
'read_direct_project_conversation',
'stop_local_game_preview_if_matches',
'start_game_creator_external_mcp',
'stop_game_creator_external_mcp',
];
const sourceExtensions = new Set([
'.json',
@@ -1753,9 +1757,8 @@ for (const snippet of [
"'read_game_creator_app_config'",
"'write_game_creator_app_config'",
'aria-label="运行时配置"',
'LLM API Key',
'External Editor Base URL',
'External Editor API Key',
'陶泥儿智能创作(固定)',
'官方账号服务(固定)',
'runtime_config.save',
"'/run:运行自检,启动本地 HTTP 预览并载入客户端运行视图'",
"'activate_local_game_preview'",
@@ -26,6 +26,7 @@ const repositoryRoot = path.resolve(appRoot, '..', '..');
const cargoManifestPath = path.join(appRoot, 'src-tauri', 'Cargo.toml');
const defaultConfigPath = path.join(appRoot, configFileName);
const localConfigFileName = 'game-creator.config.local.json';
const gameCreatorConfigSchemaVersion = 'game-creator-config.v2';
const cargoCommand = process.platform === 'win32' ? 'cargo.exe' : 'cargo';
const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
@@ -211,6 +212,7 @@ export function buildGameCreatorWizardConfig(existingConfig, llmInput) {
}
return {
...source,
schemaVersion: gameCreatorConfigSchemaVersion,
agentMode: 'provider',
llm: {
...previousLlm,
@@ -7,6 +7,7 @@ import path from 'node:path';
import { fileURLToPath } from 'node:url';
const appRoot = fileURLToPath(new URL('..', import.meta.url));
const inheritedChildEnvironment = { ...globalThis['process']['env'] };
const localConfigPath = path.join(appRoot, 'game-creator.config.local.json');
const projectRoot = path.join(
os.tmpdir(),
@@ -671,6 +672,11 @@ function runAgent() {
{
cwd: appRoot,
stdio: ['pipe', 'pipe', 'pipe'],
env: {
...inheritedChildEnvironment,
// 该 smoke 只使用一次性 loopback Provider;生产路由仍保持锁定。
GENARRATIVE_AGC_DEBUG_PROVIDER_E2E: '1',
},
},
);
let stdout = '';
@@ -127,6 +127,39 @@ function readBackendTargets({ requireAgcBackend = false } = {}) {
});
}
function readBackendServiceFailure(
state,
{
expectedDatabase = backendDatabase,
expectedSpacetimeDataDir = backendSpacetimeDataDir,
} = {},
) {
const targets = resolveBackendTargetsFromState(state, {
requireAgcBackend: true,
expectedDatabase,
expectedSpacetimeDataDir,
});
if (!targets.hasMatchingBackend) {
return null;
}
for (const serviceName of ['spacetime', 'api-server', 'bgfilter-worker']) {
const service = state?.services?.[serviceName];
if (service?.status !== 'failed') {
continue;
}
return {
serviceName,
failure: service.signal
? `signal=${service.signal}`
: `code=${service.exitCode ?? 1}`,
};
}
return null;
}
async function isBackendReady({
state = readJson(devStackStatePath),
isReady = isHttpReady,
@@ -505,11 +538,29 @@ async function terminateChildTree(
return { stopped, forced: true };
}
async function waitForBackendReady(backendChild, timeoutMs = 600_000) {
async function waitForBackendReady(
backendChild,
timeoutMs = 600_000,
{
checkBackendReady = isBackendReady,
readState = () => readJson(devStackStatePath),
resolveTargets = readBackendTargets,
} = {},
) {
const initialStateUpdatedAt = readState()?.updatedAt ?? '';
const startedAt = Date.now();
while (Date.now() - startedAt < timeoutMs) {
if (await isBackendReady()) {
return readBackendTargets();
if (await checkBackendReady()) {
return resolveTargets();
}
const state = readState();
if ((state?.updatedAt ?? '') !== initialStateUpdatedAt) {
const serviceFailure = readBackendServiceFailure(state);
if (serviceFailure) {
throw new Error(
`配套后端启动失败: ${serviceFailure.serviceName} ${serviceFailure.failure}`,
);
}
}
const failure = readChildFailure(backendChild);
if (failure) {
@@ -540,6 +591,7 @@ async function ensureBackend({
backendDatabase,
'--spacetime-data-dir',
backendSpacetimeDataDir,
'--preserve-database',
'--no-interactive',
],
{ cwd: appRoot },
@@ -680,11 +732,14 @@ function isDirectModuleExecution() {
export {
ensureBackend,
formatChildFailure,
isAiGameCreatorServer,
isBackendReady,
isDirectModuleExecution,
isProcessGroupAlive,
preflightExistingVite,
readBackendServiceFailure,
readChildFailure,
readExistingViteServer,
readLinuxProcessGroupAlive,
resolveBackendTargetsFromState,
runWindowsTaskkill,
@@ -7,7 +7,10 @@ import {
withAgcDevEndpointEnv,
} from './dev-port.mjs';
import {
isAiGameCreatorServer,
preflightExistingVite,
readChildFailure,
readExistingViteServer,
spawnChild,
stopChild,
terminateChildTree,
@@ -20,7 +23,9 @@ const tauriCliPath = resolve(repoRoot, 'node_modules/@tauri-apps/cli/tauri.js');
function buildTauriArguments(argv, devUrl = readAgcDevEndpoint().url) {
const args = [...argv];
const configOverride = JSON.stringify({ build: { devUrl } });
const configOverride = JSON.stringify({
build: { devUrl, beforeDevCommand: '' },
});
const separatorIndex = args.indexOf('--');
if (separatorIndex < 0) {
return ['dev', ...args, '--config', configOverride];
@@ -51,6 +56,7 @@ async function runTauriDev(
{
resolveDevEndpoint = resolveAgcDevEndpoint,
preflight = preflightExistingVite,
prepareFrontend = prepareFrontendDev,
spawnCli = spawnTauriCli,
waitForCli = waitForChildTermination,
terminateTree = terminateChildTree,
@@ -59,10 +65,9 @@ async function runTauriDev(
const endpoint = await resolveDevEndpoint();
await preflight({ endpoint });
const tauriArguments = buildTauriArguments(argv, endpoint.url);
const child = spawnCli(tauriArguments, {
env: withAgcDevEndpointEnv(endpoint),
});
let child = null;
let frontendChild = null;
const preparationAbort = new AbortController();
let resolveShutdown;
let shutdownSignal = '';
let repeatedSignal = false;
@@ -76,21 +81,47 @@ async function runTauriDev(
if (!shutdownSignal) {
shutdownSignal = signal;
stopChild(child, 'SIGTERM');
stopChild(frontendChild, 'SIGTERM');
preparationAbort.abort();
resolveShutdown(signal);
return;
}
repeatedSignal = true;
stopChild(child, 'SIGKILL');
stopChild(frontendChild, 'SIGKILL');
};
signalHandlers.set(signal, handler);
process.on(signal, handler);
}
try {
const preparation = prepareFrontend(endpoint, {
signal: preparationAbort.signal,
onChild(frontend) {
frontendChild = frontend;
},
});
const prepared = await Promise.race([
preparation.then(() => true),
shutdownRequested.then(() => false),
]);
if (!prepared || shutdownSignal) return 1;
const tauriArguments = buildTauriArguments(argv, endpoint.url);
child = spawnCli(tauriArguments, {
env: withAgcDevEndpointEnv(endpoint),
});
const childResult = waitForCli(child);
const outcome = await Promise.race([
childResult.then((failure) => ({ type: 'exit', failure })),
shutdownRequested.then((signal) => ({ type: 'signal', signal })),
...(frontendChild
? [
waitForChildTermination(frontendChild).then((failure) => ({
type: 'frontend-exit',
failure,
})),
]
: []),
]);
const cleanup = await terminateTree(child, {
gracefulTimeoutMs: repeatedSignal ? 0 : 2500,
@@ -105,15 +136,51 @@ async function runTauriDev(
if (outcome.type === 'signal') {
return 1;
}
if (outcome.type === 'frontend-exit') return 1;
const { failure } = outcome;
return failure.type === 'error' || failure.signal ? 1 : (failure.code ?? 0);
} finally {
for (const [signal, handler] of signalHandlers) {
process.off(signal, handler);
}
preparationAbort.abort();
if (frontendChild) {
const cleanup = await terminateTree(frontendChild);
if (!cleanup.stopped) {
console.error('[ai-game-creator-shell] 配套开发服务未能完全停止。');
}
}
}
}
async function prepareFrontendDev(endpoint, { onChild, signal }) {
const frontend = spawnChild(
process.platform === 'win32' ? 'npm.cmd' : 'npm',
['run', 'agc:serve'],
{ cwd: repoRoot, env: withAgcDevEndpointEnv(endpoint) },
);
onChild(frontend);
console.log(
'[ai-game-creator-shell] 正在准备前端与配套后端,完成后启动 Tauri',
);
const deadline = Date.now() + 660_000;
while (Date.now() < deadline) {
signal.throwIfAborted();
const failure = readChildFailure(frontend);
if (failure) {
throw new Error(
`配套开发服务退出,前端未就绪:${failure.error?.message ?? failure.signal ?? failure.code}`,
);
}
if (isAiGameCreatorServer(await readExistingViteServer(endpoint))) return;
await Promise.race([
new Promise((resolveWait) => setTimeout(resolveWait, 1000)),
waitForChildTermination(frontend),
]);
}
throw new Error(`等待前端与配套后端就绪超时:${endpoint.url}`);
}
function isDirectModuleExecution() {
return Boolean(
process.argv[1] &&
File diff suppressed because it is too large Load Diff
@@ -1,21 +1,22 @@
---
name: agc-web-game-development
description: Build or modify a playable web game in the current AGC project. Use for gameplay creation, bug fixes, UI or layout changes, responsive behavior, asset integration, controls, scoring, reset flows, and other HTML, CSS, JavaScript, DOM, Canvas, or WebGL work.
description: Build or modify a playable npm-managed Phaser 4 web game in the current AGC project. Use for gameplay creation, bug fixes, UI or layout changes, responsive behavior, asset integration, controls, scoring, reset flows, and other HTML, CSS, JavaScript, DOM, Canvas, or WebGL work.
---
# AGC Web Game Development
Implement the user's actual game request in the current project. Choose DOM, Canvas, WebGL, or a combination based on the game rather than a fixed code template.
Implement the user's actual game request in the current project as an npm-managed Phaser 4.2.1 game. Use Phaser scenes for gameplay and DOM only for deliberately external UI.
## Workflow
1. Read the existing `index.html`, `style.css`, and `game.js` before modifying an existing game.
2. Keep the entry self-contained and runnable from the AGC loopback preview. Avoid CDN-only dependencies and network-required runtime assets.
3. Build a complete playable loop: visible objective, responsive input, meaningful state changes, success or failure feedback, and a reliable restart path where the game needs one.
4. Fit the active game scene to desktop and mobile viewports without accidental page scrollbars. Reserve deliberate safe space for HUD elements instead of covering interactive content.
5. Reuse registered Taonier art when available through `agc_tools`. Load media defensively and keep gameplay usable when an optional derivative is absent; never relabel a local placeholder as platform art.
6. Avoid undefined animation callbacks, duplicate loops, stale event listeners, and state that survives restart unintentionally.
7. After a meaningful game change, use the browser playtest Skill and fix issues shown by real evidence before reporting completion.
1. Read the existing package and source files before editing. New projects place `package.json`, `index.html`, `style.css`, and `game.js` under `game/`; existing root packages retain their layout. Run npm in that package directory (for example `npm --prefix game ci` and `npm --prefix game run build`).
2. Keep `package.json` and `package-lock.json` authoritative. Import Phaser with `import Phaser from 'phaser'`; do not copy a bundle, add an import map, or use a CDN. Other npm dependencies are allowed when the game needs them.
3. Build with the project's npm script before previewing. The playable entry is the package directory's `dist/index.html`; never report an unbuilt bare-module page as playable. Import assets or configure public assets so all runtime media is included in dist; preview and exports cannot read outside it.
4. Build a complete playable loop: visible objective, responsive input, meaningful state changes, success or failure feedback, and a reliable restart path where the game needs one.
5. Fit the active game scene to desktop and mobile viewports without accidental page scrollbars. Reserve deliberate safe space for HUD elements instead of covering interactive content.
6. Reuse registered Taonier art when available through `agc_tools`. Load media defensively and keep gameplay usable when an optional derivative is absent; never relabel a local placeholder as platform art.
7. Let Phaser own the render loop and input dispatch. Avoid duplicate scenes, stale event listeners, and state that survives restart unintentionally.
8. After a meaningful game change, use the browser playtest Skill and fix issues shown by real evidence before reporting completion.
When implementing a new game loop or a broad gameplay revision, read `references/game-quality-checklist.md`.
@@ -1,4 +1,4 @@
interface:
display_name: "Web 游戏实现"
short_description: "在当前项目内设计、实现并验证可玩的 HTML、CSS 与 JavaScript 游戏"
short_description: "在当前项目内设计、实现并验证 npm 管理的 Phaser 4 游戏"
default_prompt: "Use $agc-web-game-development to build or modify the current playable web game."
@@ -1,6 +1,6 @@
{
"schemaVersion": "agc-skill-pack.v1",
"version": "2026-08-26.10",
"version": "2026-08-26.12",
"skills": [
{
"name": "agc-project-structure",
@@ -57,7 +57,7 @@
"agents/openai.yaml",
"references/game-quality-checklist.md"
],
"sha256": "d7748d9ebf4324add0541daf16a2bbec09c4862b85af55bfb369c7f3b99aedff"
"sha256": "0649c72dd53e05ad7c87b28def1397c2badf61b0c308091196c40f7c48a8b36a"
},
{
"name": "agc-browser-playtest",
@@ -14,6 +14,8 @@ mod codex_cli;
mod codex_provider_proxy;
mod direct_codex_attachments;
mod direct_codex_audit;
mod direct_project_history;
mod direct_project_turn_history;
mod direct_runtime;
mod direct_tool_bridge;
mod direct_tools_mcp;
@@ -38,6 +40,8 @@ pub(crate) use codex_cli::{
pub(crate) use codex_provider_proxy::*;
pub(crate) use direct_codex_attachments::*;
pub(crate) use direct_codex_audit::*;
pub(crate) use direct_project_history::*;
pub(crate) use direct_project_turn_history::*;
pub(crate) use direct_runtime::*;
pub(crate) use direct_tool_bridge::*;
pub(crate) use direct_tools_mcp::*;

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