Compare commits

...

71 Commits

Author SHA1 Message Date
suzmii f056c8a325 Merge remote-tracking branch 'origin/master' into fix/acl-elevation-single-flight
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m50s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m9s
Project CI / Backend tests (pull_request) Successful in 4m43s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 9m14s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 10m4s
Project CI / Frontend tests (pull_request) Successful in 2m27s
Project CI / Native shell tests (pull_request) Successful in 6m16s
Project CI / Repository checks (pull_request) Successful in 2m4s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m45s
2026-09-23 20:13:56 +08:00
suzmii d94ee96836 补一条用例:Rust 侧取消 UAC 的稳定标记同样不触发重试
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
- tests/recentProjectsHook:AGC_ACL_ELEVATION_DENIED 这类失败只检查一次,钉住 Rust 错误标记与前端「不可重试」判定之间的契约
2026-09-23 20:11:34 +08:00
kdletters 1b05a1d05e 后台充值订单实付口径、发放泥点列、用户累计充值与兑换码单位
Project CI / AI game creator shell Rust smoke (push) Successful in 2m17s
Project CI / Backend tests (push) Successful in 4m59s
Project CI / AI game creator shell Rust lane 2/2 (push) Successful in 7m29s
Project CI / AI game creator shell Rust lane 1/2 (push) Successful in 8m40s
Project CI / Frontend tests (push) Successful in 1m59s
Project CI / AI game creator shell Rust crates (push) Successful in 9m16s
Project CI / AI game creator shell web tests (push) Successful in 1m41s
Project CI / Repository checks (push) Successful in 4m4s
Project CI / Native shell tests (push) Successful in 7m1s
- 新增 AdminRechargeOrderEntryPayload.paidAmountCents:只有 paid_at 存在的订单才有实付,未支付 / 已关闭 / 已过期固定为 0
- 充值管理列表把「金额 / 泥点」拆成「实付」「发放泥点」两列,未支付行实付显示「未支付」并附订单金额小字;退款面板「订单实付」改读同一字段
- 用户详情充值订单表新增「发放泥点」列,商品列只保留商品名,实付同样按 paidAmountCents 展示
- 用户详情新增 cumulativeRechargedCents:api-server 按 user_id 读取 profile_recharge_order,只累加 paid_at 存在的订单金额(退款不回减),单次上限 500 行,读取失败或命中上限返回 null
- 用户详情身份区新增「累计充值」,读不到时显示「未知」,不用用户详情最多 20 条订单在 BFF 或前端近似重算
- 兑换码奖励单位收口为泥点:输入标签与列表列头改「奖励泥点」,单元格带「泥点」单位,避免被当成元
- 新增后端 2 条累计充值口径单测与前端 2 条用例(未支付不显示实付且发放泥点独立成列、累计充值未知态)
- 同步后端架构数据契约(实付口径 / 累计充值来源与上限 / 兑换码奖励单位)与决策记录
- 验证:cargo check -p api-server;cargo test -p api-server --bin api-server admin(138 passed / 0 failed / 1 ignored);npm run admin-web:typecheck;npx vitest run apps/admin-web/src(220 passed);cargo fmt --all --check、check:encoding、check:doc-index、git diff --check 通过
2026-09-23 20:10:20 +08:00
suzmii c079003719 Merge remote-tracking branch 'origin/master' into fix/acl-elevation-single-flight
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
# Conflicts:
#	docs/project-memory/shared-memory/decision-log.md
#	docs/project-memory/shared-memory/pitfalls.md
2026-09-23 19:59:41 +08:00
kdletters 0dbd279dfc 后台 Dashboard 消耗泥点改为对冲退还后的净消耗
Project CI / AI game creator shell Rust crates (push) Successful in 1m22s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m1s
Project CI / Backend tests (push) Successful in 5m16s
Project CI / AI game creator shell Rust lane 1/2 (push) Failing after 6m42s
Project CI / AI game creator shell Rust lane 2/2 (push) Successful in 8m21s
Project CI / Native shell tests (push) Successful in 6m43s
Project CI / Frontend tests (push) Successful in 2m22s
Project CI / Repository checks (push) Successful in 2m45s
Project CI / AI game creator shell web tests (push) Successful in 2m7s
- consumedMudPoints 改为净额:按北京时间业务日先抵当日消耗,不足再回溯抵扣最近仍有净额的业务日,抵扣不完的丢弃,每日净额非负且区间合计严格等于「消耗 − 退还」
- 退还口径并入 asset_operation_refund 正向流水(生成失败退还、精选审核返还)与 llm_router_consume 正向冲正流水;充值退款追回、余额重置、赠送和 hold 仍不计入
- 契约新增 refundedMudPoints,后端新增「退还泥点」趋势图,后台时段数据并列「退还泥点数」卡,毛消耗可由「消耗 + 退还」核出
- 新增 4 条净额单测(同日对冲 / 回溯抵扣 / 超额归零 / 无退还保原值)与 Dashboard 用例断言
- 同步 Dashboard 运营看板方案指标口径与决策记录
- 验证:cargo test -p api-server --bin api-server admin(136 passed / 0 failed / 1 ignored)、npm run admin-web:typecheck、npx vitest run apps/admin-web/src(218 passed)、cargo fmt --all --check、npm run check:encoding、npm run check:doc-index、git diff --check 全部通过
2026-09-23 19:56:24 +08:00
kdletters eb192eb161 修复渠道配置替换窗口契约导致的系统标题栏回归与登录请求被拒
- 渠道 --config 改为从基线 tauri.conf.json 读取完整 client 窗口对象后展开、只覆盖 title,避免 Tauri JSON Merge Patch 整体替换 app.windows 丢掉 label / decorations / 尺寸
- build-release.test.mjs 新增合并守卫用例:按同一 merge patch 语义复现 Tauri 合并,断言 label=client、decorations=false、1280x800、min 1280x720,且承载 http:default 的 capability 必须包含该 label
- check-config.mjs 增补基线 client 窗口 decorations 必须为 false 的门禁
- 更新 AGC 客户端更新检查与下载技术方案,写明渠道配置必须下发完整窗口对象的约定
- pitfalls.md 记录本次回归的现象、根因、现行口径与验证证据
2026-09-23 19:54:11 +08:00
suzmii af099abbfa AGC ACL 提权修复按目标做 single-flight,避免并发重复弹 UAC
- 新增 acl_repair_gate:以 (规范化 repair target, scope) 为 key 的进程级 single-flight 与结果冷却(成功 30s / 失败 15s / 用户取消 120s),等待窗口 60s 超时按失败关闭
- acl_repair_gate:leader 异常退出由 RAII 兜底记为失败并唤醒全部等待者,避免等待者被永久挂住
- config:提权修复改经闸门执行;用户取消 UAC 统一返回稳定标记 AGC_ACL_ELEVATION_DENIED,不再依赖中文文案判定
- commands/main:新增 clear_game_creator_acl_elevation_denials,供用户主动操作时解除拒绝记忆
- useRecentProjects:识别新的稳定标记;打开/新建项目与重命名刷新时清除提权拒绝记忆
- tests/acl_repair_gate:并发只执行一次、冷却复用、清除后可重试、follower 超时、leader panic 唤醒等待者
- docs:decision-log 与 pitfalls 记录 single-flight 取舍与未做项
2026-09-23 19:44:06 +08:00
lhk229 9bd4a1734c 修复策划回复重复播放并保留伪流式 (#501)
Project CI / AI game creator shell Rust crates (push) Successful in 1m24s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m57s
Project CI / Backend tests (push) Successful in 5m7s
Project CI / AI game creator shell Rust lane 2/2 (push) Successful in 8m8s
Project CI / Native shell tests (push) Successful in 6m15s
Project CI / Frontend tests (push) Successful in 2m12s
Project CI / AI game creator shell Rust lane 1/2 (push) Successful in 9m27s
Project CI / AI game creator shell web tests (push) Successful in 1m39s
Project CI / Repository checks (push) Successful in 2m20s
按消息 ID 统一策划回复呈现,隔离旧回合收尾并补全忙碌态清理;同步回归测试与文档。已合并 origin/master 并通过定向验证。
2026-09-23 19:19:26 +08:00
lhk229 248003a445 合并主线更新并保留双方排障记录
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m24s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m1s
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
合并 origin/master 的上传续传、标题栏弹窗及测试更新。

解决排障文档冲突,保留策划回复与标题栏问题记录。
2026-09-23 11:18:46 +00:00
kdletters f0ef068910 修正标题栏品牌标签用例对渠道产品名的断言
Project CI / AI game creator shell Rust smoke (push) Successful in 1m37s
Project CI / AI game creator shell Rust crates (push) Successful in 1m18s
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (push) Has been cancelled
- WindowChrome 用例改用 appMetadata 的 APP_NAME(dev 渠道为陶泥儿开发版),不再硬编码旧产品名
2026-09-23 19:07:37 +08:00
kdletters c2c5e1ced5 发行包上限提升到 200 MiB 并支持 AGC 分片续传上传
- 发行包上限 100→200 MiB、展开总量 250→500 MiB,整包路由请求体上限继续从包上限派生;发行静态资源进程内缓存预算提到 256 MiB
- 反代放行量同步放宽到 210 MiB:Nginx 三份模板的 client_max_body_size、Pingora 网关默认值与 env 样例、路由对照矩阵
- platform-oss 新增内部对象追加写 append_internal_object(_with_retry),以 OSS 返回的 next-append-position 作为权威已收字节
- api-server 新增 upload-state / chunk / complete / reset 四条分片路由,抽出共享收口 confirm_validated_package;偏移不符返回 409 与权威偏移,校验失败删除半包并落 upload_failed
- AGC 新增原生上传器 game_package_upload.rs(内容寻址暂存、分片续传、受控重试、进度事件)与 prepare / upload 两条命令,退役整包回传命令
- 渲染进程改为 prepare → 创建游戏 → 创建版本 → 原生分片上传 → 送审,LocalProjectExportPackagePayload 整包类型退役
- 同时修正 live 用例无法指向本地栈的两处基础设施问题:平台基址按传入 URL 选择,桥接层把 jsdom realm 的 Headers / Blob / FormData 降级成 Node 原生值
- 测试:platform-oss 74、api-server game_distribution 23、AGC 原生 4、发布相关前端 20;live 用例补真实栈「中断 → 续传 → 确认」断言(分片偏移序列 [0, 8388608])
- 文档:玩法创作主规范的上传合同、运维与 Pingora 文档、决策记录、发行里程碑口径,以及新增的续传里程碑与实施计划
2026-09-23 19:06:02 +08:00
kdletters c38d07044a 修复 AGC 弹窗打开时自绘标题栏窗口按钮失效
- ThemedModal 的焦点陷阱只放行落在 [data-window-chrome-bar] 内的点击,标题栏拖拽与最小化/最大化/关闭恢复可用,工作区内容点击仍被拦住
- WindowChrome 标题栏加 data-window-chrome-bar 标记,作为这条约定的唯一契约点
- styles.css 明确「全屏弹层一律从标题栏下方开始」,.app-update-overlay 从 inset:0 改为标题栏下方,.game-publish-progress-overlay 显式声明 top
- 新增 tests/windowChromeOverlayContract.test.ts 覆盖 7 个全屏弹层;themedModal / WindowChrome 用例补「标题栏点击放行 + 工作区点击仍被拦」回归;gamePublishFeedback 用例按新口径断言
- pitfalls 记录该静默失效的机制与现行口径
2026-09-23 19:05:33 +08:00
lhk229 efbfc8085d Merge branch 'master' into fix/design-agent-fe
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m19s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 57s
Project CI / Backend tests (pull_request) Successful in 5m11s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 7m33s
Project CI / Native shell tests (pull_request) Successful in 6m43s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 9m7s
Project CI / Frontend tests (pull_request) Successful in 3m1s
Project CI / Repository checks (pull_request) Successful in 3m23s
Project CI / AI game creator shell web tests (pull_request) Failing after 2m37s
2026-09-23 19:04:31 +08:00
lhk229 2c778fb027 补全聊天作用域重置时的忙碌态清理
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
重置聊天状态时同步清空 chatAgentBusy,保留旧请求的回合归属检查。

同步更新策划会话的作用域清理约定。
2026-09-23 11:03:03 +00:00
kdletters ebb288a6a4 统一客户端渠道产品名称
Project CI / AI game creator shell Rust smoke (push) Successful in 1m28s
Project CI / AI game creator shell Rust crates (push) Successful in 59s
Project CI / Backend tests (push) Successful in 4m50s
Project CI / Native shell tests (push) Successful in 5m43s
Project CI / AI game creator shell Rust lane 2/2 (push) Successful in 7m23s
Project CI / AI game creator shell Rust lane 1/2 (push) Successful in 8m55s
Project CI / Frontend tests (push) Successful in 2m8s
Project CI / AI game creator shell web tests (push) Failing after 1m52s
Project CI / Repository checks (push) Successful in 2m29s
dev 渠道显示为陶泥儿开发版并保持既有 identifier 与升级链。

将产品名注入 Tauri、原生窗口、前端标题栏和关于页面,统一快捷方式与注册表展示来源。

补充渠道配置门禁、发布脚本测试和渠道身份文档。
2026-09-23 18:53:23 +08:00
k88936 661867c307 Merge pull request '功能:引用输入区支持粘贴解析 @显示名 / $名称' (#473) from feat/smart-paste into master
Project CI / AI game creator shell Rust lane 2/2 (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (push) Has been cancelled
Reviewed-on: #473
Reviewed-by: 孔令弘 <ink29535@proton.me>
2026-09-23 18:51:23 +08:00
k88936 c0e233fe6c Merge remote-tracking branch 'origin/master' into feat/smart-paste
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
2026-09-23 18:50:15 +08:00
k88936 136e1ebceb Remove redundant fuzzy_lookup methods from test ReferenceProviders for cleanup
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
2026-09-23 18:49:07 +08:00
lhk229 688cf7a971 修复策划回复重复播放并保留伪流式
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m28s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m59s
Project CI / Backend tests (pull_request) Successful in 5m1s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 8m7s
Project CI / Native shell tests (pull_request) Successful in 6m17s
Project CI / Frontend tests (pull_request) Successful in 2m9s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 9m29s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m42s
Project CI / Repository checks (pull_request) Successful in 2m18s
按消息 ID 合并实时正文与正式消息,保留逐步显示进度
分离请求收尾与动画结束,隔离旧回合事件及异步返回
独立展示工具状态,保留 Provider 重试的正文重置语义
补充重复终态、整块回复、多消息及回合隔离回归测试
同步更新策划展示规范与排障记录
2026-09-23 10:38:01 +00:00
lhk229 c2ec978199 修复宿主继续请求重复发送原始用户输入 (#500)
Project CI / AI game creator shell Rust crates (push) Successful in 1m23s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m0s
Project CI / Backend tests (push) Successful in 4m51s
Project CI / Native shell tests (push) Successful in 5m57s
Project CI / AI game creator shell Rust lane 2/2 (push) Successful in 8m2s
Project CI / Frontend tests (push) Successful in 2m1s
Project CI / AI game creator shell Rust lane 1/2 (push) Successful in 9m7s
Project CI / AI game creator shell web tests (push) Successful in 1m20s
Project CI / Repository checks (push) Successful in 1m43s
区分首次用户请求与宿主反馈,继续时发送实际验收或错误信息
固定 GUI 和 CLI 的原始用户条目,保留历史与回合身份关联
补充结构化输入、反馈发送、图片输入和历史去重的定向回归测试
同步更新实施计划与排障记录,保持交付验收条件和预算不变

Reviewed-on: https://git.genarrative.world/git/GenarrativeAI/Genarrative/pulls/500
Co-authored-by: Linghong <ink29535@proton.me>
Co-committed-by: Linghong <ink29535@proton.me>
2026-09-23 18:20:42 +08:00
k88936 58c59b750e Merge remote-tracking branch 'origin/master' into feat/smart-paste
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m14s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 56s
Project CI / Backend tests (pull_request) Successful in 4m42s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m4s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 8m15s
Project CI / Native shell tests (pull_request) Successful in 6m1s
Project CI / Frontend tests (pull_request) Successful in 2m26s
Project CI / Repository checks (pull_request) Successful in 2m33s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m31s
# Conflicts:
#	docs/project-memory/shared-memory/decision-log.md
2026-09-23 17:52:49 +08:00
kdletters 297d7bf1e7 优化 AGC 发布资料与截图交互
Project CI / AI game creator shell Rust crates (push) Successful in 1m24s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m56s
Project CI / Backend tests (push) Successful in 4m39s
Project CI / Native shell tests (push) Successful in 5m49s
Project CI / Frontend tests (push) Successful in 2m1s
Project CI / AI game creator shell Rust lane 2/2 (push) Successful in 8m29s
Project CI / AI game creator shell Rust lane 1/2 (push) Successful in 8m31s
Project CI / AI game creator shell web tests (push) Successful in 1m53s
Project CI / Repository checks (push) Successful in 2m25s
发布门禁阻断提示改为独立弹窗且不写入聊天记录
移除发布面板发行包技术摘要
新增免费发布简介与分类建议接口和前端回填
接入封面泥点定价、确认与生成流程
截图改为并行上传,单张失败隔离且不阻断发布
截图操作改为图标,失败项内嵌错误与删除
同步发布合同、实施计划和决策记录
2026-09-23 17:40:08 +08:00
suzmii 64018f9661 Merge pull request '修复 AGC 最近项目「检查失败」被钉死:失败退避重试 + 按轮重查(#490)' (#492) from fix/agc-recent-project-status-retry into master
Project CI / AI game creator shell Rust crates (push) Successful in 1m22s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m50s
Project CI / Backend tests (push) Successful in 3m52s
Project CI / AI game creator shell Rust lane 1/2 (push) Failing after 5m48s
Project CI / Frontend tests (push) Successful in 2m24s
Project CI / Native shell tests (push) Successful in 6m22s
Project CI / AI game creator shell Rust lane 2/2 (push) Successful in 8m14s
Project CI / Repository checks (push) Successful in 2m32s
Project CI / AI game creator shell web tests (push) Successful in 2m7s
Reviewed-on: #492
Reviewed-by: 孔令弘 <ink29535@proton.me>
2026-09-23 17:30:59 +08:00
suzmii b48953f87c Merge remote-tracking branch 'origin/fix/agc-recent-project-status-retry' into fix/agc-recent-project-status-retry
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m30s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m9s
Project CI / Backend tests (pull_request) Successful in 5m2s
Project CI / Native shell tests (pull_request) Successful in 6m57s
Project CI / Frontend tests (pull_request) Successful in 2m35s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 9m19s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 9m23s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m23s
Project CI / Repository checks (pull_request) Successful in 1m48s
2026-09-23 17:13:23 +08:00
suzmii 924d5d3fd0 Merge remote-tracking branch 'origin/master' into fix/agc-recent-project-status-retry
# Conflicts:
#	docs/project-memory/shared-memory/decision-log.md
2026-09-23 17:10:38 +08:00
lhk229 5bc5328c06 客户端埋点复用已有下载渠道配置 (#496)
Project CI / AI game creator shell Rust crates (push) Successful in 1m18s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m55s
Project CI / Backend tests (push) Successful in 3m42s
Project CI / Frontend tests (push) Successful in 1m44s
Project CI / Native shell tests (push) Successful in 5m37s
Project CI / AI game creator shell Rust lane 2/2 (push) Successful in 8m24s
Project CI / AI game creator shell Rust lane 1/2 (push) Successful in 8m30s
Project CI / Repository checks (push) Successful in 2m0s
Project CI / AI game creator shell web tests (push) Successful in 1m31s
删除独立埋点 origin 环境变量及配置字段,按 dev/release 渠道校验官方站点。
本地开发、测试及容器的 dev 渠道允许规范回环地址,兼容可变端口。
同步环境变量示例、技术方案和运维说明,补充渠道隔离与身份校验测试。

Reviewed-on: https://git.genarrative.world/git/GenarrativeAI/Genarrative/pulls/496
2026-09-23 17:03:17 +08:00
suzmii e6ed3cae2c Merge remote-tracking branch 'origin/master' into fix/agc-recent-project-status-retry
# Conflicts:
#	docs/project-memory/shared-memory/decision-log.md
2026-09-23 16:54:56 +08:00
kdletters e072c66ce9 修复 AGC 模板库筛选与卡片状态的表现缺陷
Project CI / AI game creator shell Rust lane 1/2 (push) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
- 模板库筛选区改用共享筛选条与标签 chip:选中为实心品牌填充 + 反白文字,未选为浅底描边
- 未选中悬停不再借用品牌色(品牌色专属已选中),状态阶梯固定为静止 / 悬停 / 按下 / 选中
- 新增 getPlatformCategoryChipClassName 收敛筛选 chip 类名口径,模板库、资源画布、参考图弹窗与平台 Web 端共用
- PlatformSegmentedTabs 增加 accent 选中口径,与筛选 chip 共用 --platform-chip-* 语义色
- 修复卡片文字区按 grid auto 行排版导致的标题 / 简介 / 标签被裁:行高契约拆为分项常量,文字区固定 174
- 修复忙状态下动作行塞入第三个元素导致的按钮文案换行顶出卡片:忙状态写在触发它的按钮上
- 修复经典滚动条占宽导致的卡片区横向滚动条:按竖滚动条预留列宽后再算列宽行高
- 焦点环由 15% 透明度改为实心色并用 outline 绘制,选中项聚焦不再被状态投影盖掉
- 封面加载失败改为隐藏图片留中性底色,不再出现浏览器裂图图标
- 补充状态对比度、行高契约、滚动条预留、封面兜底与忙状态文案的回归测试
- 同步技术方案、踩坑记录与决策记录的口径
2026-09-23 16:51:15 +08:00
suzmii 5bdf25cfd7 评审收口:提权类失败不再重试、重命名路径复用重试、退避预算绑定失败集合
- useRecentProjects:区分可重试失败与提权/权限类失败,后者不再就地重试、也不驱动整表重查,避免自己驱动 UAC 反复弹窗
- useRecentProjects:提权类失败的项目在用户主动打开/新建项目或重命名刷新前跳过检查
- useRecentProjects:退避重查预算绑定到失败集合,集合变化或整轮无失败即重置,长期坏目录不再吃满额度
- useRecentProjects:重命名后的单条刷新复用同一套就地重试与有界重查
- app/tauri:删除重复的 TauriInvoke 类型,统一引用 app/types 的既有定义
- tests/recentProjectsHook:失败不跨轮保留改为断言在途「检查中」,新增提权类失败不重试用例
- docs:decision-log、pitfalls、生命周期方案与异步闭环方案同步失败自愈口径与提权边界
2026-09-23 16:41:10 +08:00
k88936 e8d987c207 Merge pull request 'refactor/收口退役AGC项目对话斜杠命令与终端swarm chat入口' (#488) from fix/clean-history-cmd into master
Project CI / AI game creator shell Rust crates (push) Successful in 1m21s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m43s
Project CI / Backend tests (push) Successful in 3m47s
Project CI / Frontend tests (push) Successful in 2m23s
Project CI / Native shell tests (push) Successful in 6m14s
Project CI / AI game creator shell Rust lane 1/2 (push) Successful in 8m13s
Project CI / AI game creator shell Rust lane 2/2 (push) Successful in 9m19s
Project CI / Repository checks (push) Successful in 2m9s
Project CI / AI game creator shell web tests (push) Successful in 1m45s
Reviewed-on: #488
2026-09-23 16:34:03 +08:00
suzmii cfd41b5a0d Merge remote-tracking branch 'origin/master' into fix/agc-recent-project-status-retry 2026-09-23 16:24:13 +08:00
kdletters f426944701 调整 AGC 更新弹窗并停止自动生成更新日志
Project CI / AI game creator shell Rust crates (push) Successful in 1m18s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m46s
Project CI / Backend tests (push) Successful in 3m36s
Project CI / Frontend tests (push) Successful in 1m43s
Project CI / Native shell tests (push) Successful in 5m40s
Project CI / AI game creator shell Rust lane 2/2 (push) Successful in 7m35s
Project CI / Repository checks (push) Successful in 1m50s
Project CI / AI game creator shell Rust lane 1/2 (push) Successful in 8m31s
Project CI / AI game creator shell web tests (push) Successful in 1m16s
- 更新弹窗改为窗口居中纵向排版

- 停止自动生成渠道清单更新摘要

- 仅保留 AGC_UPDATE_RELEASE_NOTES 手动更新说明

- 清理 Jenkins 摘要锚点与 macOS 额外拉取 master 逻辑

- 同步开发运维、技术方案和回归测试
2026-09-23 16:18:04 +08:00
k88936 ede6efbe73 Merge remote-tracking branch 'origin/master' into fix/clean-history-cmd
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m23s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m51s
Project CI / Backend tests (pull_request) Successful in 3m38s
Project CI / Frontend tests (pull_request) Successful in 1m55s
Project CI / Native shell tests (pull_request) Successful in 5m38s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 8m16s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m28s
Project CI / Repository checks (pull_request) Successful in 1m47s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m17s
# Conflicts:
#	docs/project-memory/shared-memory/decision-log.md
#	docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md
2026-09-23 15:32:47 +08:00
lhk229 a1dd382489 Merge branch 'master' into fix/agc-recent-project-status-retry
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m15s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m22s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Failing after 4m35s
Project CI / Backend tests (pull_request) Successful in 3m56s
Project CI / Frontend tests (pull_request) Successful in 1m41s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m40s
Project CI / Native shell tests (pull_request) Successful in 6m17s
Project CI / Repository checks (pull_request) Successful in 2m28s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m57s
2026-09-23 15:22:32 +08:00
kdletters 016356e509 合并 AGC 发布前原型门禁与进度弹窗
Project CI / AI game creator shell Rust crates (push) Successful in 1m25s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m56s
Project CI / Backend tests (push) Successful in 3m54s
Project CI / Frontend tests (push) Successful in 1m54s
Project CI / Native shell tests (push) Successful in 5m55s
Project CI / AI game creator shell Rust lane 2/2 (push) Successful in 7m56s
Project CI / AI game creator shell Rust lane 1/2 (push) Successful in 8m58s
Project CI / Repository checks (push) Successful in 1m53s
Project CI / AI game creator shell web tests (push) Successful in 1m18s
合并 codex/agc-publish-prototype-gate
2026-09-23 14:55:45 +08:00
kdletters 8dc0da4608 完善 AGC 发布前原型门禁与进度弹窗
发布前校验可运行原型,未完成时禁止触发用户项目构建

缺失发布说明时自动生成 exports/README.md

移除发布流程的聊天确认卡,新增全屏发布进度弹窗

补充前后端回归测试与项目决策文档
2026-09-23 14:47:43 +08:00
suzmii 7157a90d31 合并 master(含 #494):AGC 提示词断言同步改采 master 版本
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m22s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m52s
Project CI / Backend tests (pull_request) Successful in 3m54s
Project CI / Frontend tests (pull_request) Successful in 1m55s
Project CI / Native shell tests (pull_request) Successful in 5m58s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 8m13s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m47s
Project CI / Repository checks (pull_request) Successful in 2m1s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m26s
- 合并 origin/master(6b4c6b8a6 修复策划 Agent 提示词契约误删 #494)
- Rust 断言同步与本分支 25d331ca0 重复,冲突按 master 版本解决(#494 用「请求含该 run 任务文本」替代 run_id 断言)
- 保留本分支独有改动:最近项目检查失败退避重试、TauriInvoke 命名类型、admin-web 409 聚焦断言等待
- 丢弃本地 cargo 运行重新生成的 chat/generated/*.ts,按 master 版本还原
2026-09-23 14:46:39 +08:00
lhk229 6b4c6b8a63 修复策划 Agent 提示词契约误删 (#494)
Project CI / AI game creator shell Rust crates (push) Successful in 1m26s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m55s
Project CI / AI game creator shell Rust lane 2/2 (push) Failing after 5m15s
Project CI / Backend tests (push) Successful in 3m53s
Project CI / Frontend tests (push) Successful in 2m4s
Project CI / Repository checks (push) Successful in 2m16s
Project CI / Native shell tests (push) Successful in 6m15s
Project CI / AI game creator shell Rust lane 1/2 (push) Successful in 8m33s
Project CI / AI game creator shell web tests (push) Successful in 1m34s
恢复策划产物路径及模板、示例资源定位。
恢复决定台账 D-xx 编号与跨文档引用。
恢复验收及 Skill 标识和阶段审批成功条件。
保留战斗系统示例非必读约定。

Reviewed-on: https://git.genarrative.world/git/GenarrativeAI/Genarrative/pulls/494
Co-authored-by: Linghong <ink29535@proton.me>
Co-committed-by: Linghong <ink29535@proton.me>
2026-09-23 14:25:02 +08:00
k88936 ce70171b43 修正AGC退役后遗留的 prompt 源文件门禁与本地 Provider 冒烟断言
- prompt_source_boundaries.rs 移除已删除的 src/agent/interaction.rs 条目(否则读源文件直接 NotFound)
- smoke-agent-run-local-provider.mjs 删除已退役的画板回流建议断言,对应被删的 suggested_canvas_tool_call
2026-09-23 14:23:02 +08:00
suzmii 25d331ca0a 同步 #491 漏改的提示词断言,修 master 上两条 AGC Rust lane 的 CI 红
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m20s
Project CI / Backend tests (pull_request) Failing after 12s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m6s
Project CI / Frontend tests (pull_request) Successful in 2m3s
Project CI / Repository checks (pull_request) Failing after 12s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m23s
Project CI / Native shell tests (pull_request) Successful in 5m45s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 8m8s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m52s
- tests/provider:工具计划与修复请求的 run_id 断言反转为「不得携带」(#491 已把 runId 移出提示词)
- tests/response_stream:4 个 finalization/replan 用例同步 run_id 与 session_id 断言(同一 run 改由 runtime/DB 断言承担)
- tests/project_tools:删除确认后的重规划请求同样反转为「不得携带 run_id」
- admin-web:AGC 模板 409 写入错误用例把聚焦断言套进 waitFor,消除 CI 抢跑 flake
2026-09-23 13:28:17 +08:00
k88936 fc39080f27 Merge remote-tracking branch 'origin/master' into fix/clean-history-cmd
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Failing after 1m31s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m8s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 1m25s
Project CI / Frontend tests (pull_request) Successful in 2m17s
Project CI / Backend tests (pull_request) Successful in 3m56s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Failing after 4m58s
Project CI / Repository checks (pull_request) Successful in 2m3s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m33s
Project CI / Native shell tests (pull_request) Successful in 5m54s
# Conflicts:
#	CONTEXT.md
#	apps/ai-game-creator-shell/scripts/check-config.mjs
#	apps/ai-game-creator-shell/src/App.tsx
#	apps/ai-game-creator-shell/src/view/project-development/chat/controller/useDirectProjectChatController.ts
#	docs/README.md
#	docs/project-memory/shared-memory/decision-log.md
2026-09-23 13:27:52 +08:00
k88936 cba98be35f Merge remote-tracking branch 'origin/master' into feat/smart-paste
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m23s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m57s
Project CI / Backend tests (pull_request) Successful in 4m32s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Failing after 6m44s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Failing after 8m24s
Project CI / Frontend tests (pull_request) Successful in 2m37s
Project CI / Native shell tests (pull_request) Successful in 6m49s
Project CI / Repository checks (pull_request) Successful in 2m50s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m52s
2026-09-23 13:23:10 +08:00
k88936 28a6fd97f8 修复:润色回写与粘贴共用同一条落点兜底,认不出的引用落文本而不是消失
ResourceReferenceInput 新增 mentionTokenOrText:provider 的 mentionToken,答不出来(契约被破坏)时退 contentPartText 的通用文本形态;粘贴插入、润色回写的候选扫描、整根替换的落点共用这一条
润色回写的候选不再跳过「答不出 token 的 part」:这类 part 在回包里没被提到时作为末尾孤儿补回来,而不是在翻译阶段直接消失
applyContentToRoot(初始草稿与润色回写共用)解析不出引用时落文本,不再是整条吃掉
新增两处输入区用例:provider 只答得出引用答不出 token 时润色回写仍保住引用;初始草稿里已解析不出的引用落成 @resourceId 文本。两处去掉兜底即变红
同步 AGC 聊天素材引用功能说明、引用粘贴解析实施计划与决策记录(落点兜底一条,含初始草稿行为变化的记录)
2026-09-23 13:11:17 +08:00
k88936 f138f648e7 修复:粘贴插入解析不出引用时落下文本形态,不再什么都不插
resourceReferences.ts 抽出 contentPartToken:part → 正文 token 的逐字投影,不补白、不裁剪(补白只在 joinMentionText 拼整段文本时加);directCodexContentToPromptText 与新导出 contentPartText 都走这一份
contentPartText 就是单个非文本 part 的文本形态:资源落 @resourceId、Skill 落 $名称、附件落 @附件名、运行区域落 @标签,文本 part 返回 null
$insertContentAtSelection 的兜底链改成 provider 的 mentionToken 之外再接 contentPartText,解析不出引用也一定有一段正常文本落到光标处,删掉「两条都答不出来就什么都不插」这条违背「粘贴内容逐字保留」的分支
新增单测覆盖四类 part 的文本形态与文本 part 返回 null;新增输入区集成用例(provider 的 toReference 与 mentionToken 都答不出来时粘贴仍按 @resourceId 落下),去掉兜底即变红
同步 AGC 聊天素材引用功能说明的粘贴解析小节,并在实施计划补记本轮评审的收口
2026-09-23 13:06:43 +08:00
kdletters 97bb0673bf 合并 修复 AGC 发布入口在 DirectProject 下的反馈与确认
Project CI / AI game creator shell Rust crates (push) Successful in 1m24s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m53s
Project CI / Backend tests (push) Successful in 4m13s
Project CI / AI game creator shell Rust lane 1/2 (push) Failing after 6m31s
Project CI / Frontend tests (push) Successful in 2m39s
Project CI / AI game creator shell Rust lane 2/2 (push) Failing after 8m55s
Project CI / Native shell tests (push) Successful in 7m2s
Project CI / Repository checks (push) Successful in 2m35s
Project CI / AI game creator shell web tests (push) Successful in 1m28s
- 发布动作即时回显权限检查与构建打包状态
- DirectProject 对话渲染项目权限确认卡片并回传确认与取消
- 权限拒绝、取消和查询失败统一回显到项目对话
- 新增发布反馈回归用例并记录排障口径
2026-09-23 13:01:45 +08:00
kdletters ba45ca7048 修复 AGC 发布入口在 DirectProject 下的反馈与确认
- 发布动作即时回显权限检查与构建打包状态
- DirectProject 对话渲染项目权限确认卡片并回传确认与取消
- 权限拒绝、取消和查询失败统一回显到项目对话
- 新增发布反馈回归用例并记录排障口径
2026-09-23 12:59:17 +08:00
suzmii ceb6418877 Merge branch 'master' into fix/agc-recent-project-status-retry
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m22s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m51s
Project CI / Backend tests (pull_request) Successful in 3m40s
Project CI / Frontend tests (pull_request) Failing after 45s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Failing after 6m23s
Project CI / Native shell tests (pull_request) Successful in 5m57s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Failing after 7m57s
Project CI / Repository checks (pull_request) Successful in 2m16s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m42s
2026-09-23 12:53:17 +08:00
suzmii 04b738755f 修复 AGC 最近项目「检查失败」被钉死:失败退避重试 + 按轮重查
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m20s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 57s
Project CI / Backend tests (pull_request) Failing after 19s
Project CI / Frontend tests (pull_request) Successful in 2m3s
Project CI / Repository checks (pull_request) Failing after 14s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m20s
Project CI / Native shell tests (pull_request) Successful in 5m31s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 7m28s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m11s
- useRecentProjects:单次目录检查失败先就地重试一次(300ms),不再立刻落成失败状态
- useRecentProjects:增量投影只保留成功结果,上一轮的失败项回到「检查中」并重新检查
- useRecentProjects:一轮结束仍有失败行时按 15s/45s/120s 退避重跑整张列表,连续无失败即清零
- app/tauri:导出命名类型 TauriInvoke,替换 3 处 ReturnType<typeof resolveTauriInvoke> 写法
- tests/recentProjectsHook:新增「单次失败会重试」「刷新时重查失败项」两个用例
- tests/appSurface/home.suite:失败状态改为异步等待(重试后才会落成)
2026-09-23 12:38:04 +08:00
k88936 d019e48f58 删除AGC steer 判定链相关前端类型、prompt 键与门禁条目
- 删除前端 AgentRuntimeSteerResult 类型
- 删除 interaction.json 中只服务该判定链的 steer_decision_description / steer_decision_system / steer_decision_user 提示词键
- 移除 check-config.mjs 未调用 Tauri 命令允许清单里的 steer_game_creator_agent_runtime_task
2026-09-23 12:36:11 +08:00
k88936 ba9832a287 删除AGC无调用方的 steer Tauri 命令与 LLM steer 判定链
- 删除 Tauri 命令 steer_game_creator_agent_runtime_task,并同步 main.rs invoke_handler 注册与 AgentRuntimeSteerResult 的 assistantReply / interruptDecision / decisionReason 字段
- 删除只服务该命令的 agent/interaction.rs 整层(runtime_steer_decision 工具、判定请求构建与响应解析、decide_game_creator_agent_runtime_steer_at)及其 agent.rs 模块挂载
- 删除 runtime.interrupt_for_steer_decision RPC 与 Runner 客户端/派发分支、持久 decision 记录 agent.runtime.steer_decision、AgentRuntimeProviderInterrupt::applied_steer_cursor 及 register 的 cursor 参数
- 删除只测该链条的 provider / runner / runtime_state 用例
2026-09-23 12:35:44 +08:00
k88936 857acd4190 文档:记录退役收尾删除AGC LLM steer 判定链
- ADR 与里程碑的复查收尾改为「steer 命令删除后整条 LLM 判定链一并退役」,去掉“单独评估”的过渡口径
- Runtime V1.1 的 V1.53 与主实施计划的 2026-08-10 节新增 2026-09-23 复查收尾说明,标明判定链已删除、现役只保留 runtime.steer 唤醒
- decision-log 的 2026-08-10 条目补更新说明,并从已知失败基线用例里移除已删除的 runtime_interrupt_for_true_steer_decision_only_interrupts_older_provider_cursor
- pitfalls 的「Supervisor steer 不能只有内部排队事件」标注判定链实现已删除
- 里程碑验证段补充本轮 cargo check / typecheck / 编码与文档索引 / 幂等检索的实测结果
2026-09-23 12:35:08 +08:00
kdletters f789db7f2f 收敛 AGC 外置智能体提示词,去掉客户端与宿主实现细节 (#491)
Project CI / AI game creator shell Rust smoke (push) Successful in 1m33s
Project CI / AI game creator shell Rust crates (push) Successful in 59s
Project CI / AI game creator shell Rust lane 1/2 (push) Failing after 6m23s
Project CI / Backend tests (push) Successful in 4m52s
Project CI / AI game creator shell Rust lane 2/2 (push) Failing after 8m6s
Project CI / Frontend tests (push) Successful in 2m25s
Project CI / Native shell tests (push) Successful in 6m28s
Project CI / Repository checks (push) Successful in 2m37s
Project CI / AI game creator shell web tests (push) Successful in 1m57s
## 背景
AGC 外置智能体提示词里暴露了过多客户端/宿主实现细节。本次按「提示词只写模型必须遵守的指令与契约」的边界做收敛:删掉"我们系统怎么实现"的叙述,保留模型真正要执行的契约。

## 改动
- `prompts/runtime`:删除宿主机制叙述(交付合同冻结与自动关闭、manifest 幂等投影、回执认领与 continuation 派生、浏览器证据回灌、Runtime 自动推进阶段)
- 删除内部技术栈与部署细节(bubblewrap/Chromium profile 沙箱、执行器锁与 stage ledger、DLL/gdextension 分发、Chromium loopback 预览)
- 删除内部预算与字段(`validation.maxRuns`、共用客户端持久预算、preMutationLoopLimit、源码字符上限、错误反馈重试计数)
- 删除内部标识与名词(Runtime 元数据 `instanceId/sessionId/runId/executionMode/contextBoundary`、`DirectProject`/`workspaceMode`、`autonomous-game-build`、Run Profile、manifest DAG)
- `resources/agc-skills`:8 个 skill 的客户端职责/状态枚举叙述收敛,skill pack 版本与内容哈希同步
- `design-agent`:系统提示、阶段上下文、模板与示例中的渲染器、内部落盘路径、登记编号与平台实现措辞收敛
- 保留不动:工具名与参数、回执字段与枚举、调用顺序、禁止项、失败与鉴权处理、产物要求
- 约定写入 `docs/project-memory/shared-memory/team-conventions.md`:提示词不写宿主实现细节;改占位符须同步 Rust 调用点与断言

## 验证
- `cargo check --offline --tests`:通过
- `cargo test … prompt`:76 passed;`design`:60 passed;`autonomous_game_build`:29 passed;`tool_planning`:10 passed
- `npm run check:encoding`、`npm run check:rustfmt`、`node scripts/check-skill-pack.mjs`、`git diff --check`:均通过
- 本机既有环境失败(非本次引入,已在未改动 HEAD 上用 stash 复现):mock provider 相关的 runtime_actions 用例、isolated-registry 进程会话用例

Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/491
Co-authored-by: kdletters <kdletters@qq.com>
Co-committed-by: kdletters <kdletters@qq.com>
2026-09-23 12:31:20 +08:00
k88936 10f2f05437 文档:同步收尾清理里已删除的 Tauri 后台任务命令
- ADR 与里程碑的收尾清理段区分已删除的 start_game_creator_agent_runtime_task 与待处置的 steer 命令
2026-09-23 12:11:00 +08:00
k88936 271b7172bd 删除AGC无调用方的 Tauri 后台任务命令并清理门禁注释
- 删除 commands.rs 的 start_game_creator_agent_runtime_task(前端无调用方,内部实现与 CLI 入口保持不变)
- main.rs invoke_handler 同步移除该命令注册
- check-config.mjs 的 native-only allowlist 同步移除该条目
- check-config.mjs allowlist 注释去掉「终端 swarm CLI 也已退役」的墓碑式表述
2026-09-23 12:08:09 +08:00
k88936 5b2318acf3 删除AGC退役后零引用的真实E2E harness 导出与函数
- runtime-state.mjs 删除只被已删套件使用的 4 个 schema 常量(runProfileBinding / autonomousCompletionContract / autonomousPlaytestReceipt / autonomousGameBuildRunProfile)
- assertions/runtime.mjs 删除 runtimePublicStatusMessageId
- harness/project.mjs 删除 productionDefaultGameIndexHtml
- collaboration-policy.mjs 删除 validateSupervisorSwarmIsolatedSpawnInput 及其专属的静态隔离校验函数族
- persistence.mjs 删除 5 个 supervisorSwarmIsolated*Identity 函数
- 移除随之失去用途的 import(hashJsonValue、isolatedJoinDeliveryTarget、supervisorSwarmIsolatedReviews)
2026-09-23 12:07:51 +08:00
k88936 8d710738e2 清理AGC退役残留:配置向导悬空脚本、用户文案与孤儿 prompt 键
- 配置向导删除指向已删 npm 脚本的 `npm run test:chat` 调用与失去含义的 `--configure-only` 开关
- 删除只服务该分支的 askYesNo 与 npmCommand,保持 TTY 前置检查语义不变
- App 内用户文案 `请先用 /project 设置本地项目。` 改为 `请先打开本地项目。`
- chatPromptPolish 头部注释去掉已退役的 `/` 命令表述
- 删除 interaction.json 中只服务已删交互内核的 7 个 prompt 键(execute_description、resume_description、project_location_description、protocol、system、user、user_with_context)
- harness/process.mjs 的账号 fixture 注释改为描述现役隔离 Runner 机制
2026-09-23 12:07:37 +08:00
k88936 645ef30752 文档:修正AGC斜杠命令与终端swarm chat退役口径并记录收尾清理
- 修正 ADR 与里程碑中「agent.compact / agent.resume / agent.run_status 权限门禁消失」的过宽表述,限定为 swarm_cli/input.rs 里那份终端命令校验,现役门禁仍在
- 修正 `GAME_CREATION_APP_COMMANDS`「开发 Agent UI 仍在用」的说法为 App 只消费命令 id 类型与审计粒度
- 里程碑新增「收尾清理」小节,记录配置向导悬空脚本、用户文案、孤儿 prompt 键、harness 死导出与无调用方 Tauri 命令的清理结论
- 里程碑验证段更新为本次实测数据(typecheck、appSurface 211 tests、cargo check、编码与文档索引、harness self-test)
- AGC 渠道安装身份隔离实施计划移除已删除的 agent-swarm-test-chat.mjs
- pitfalls 去掉 `agc:test:chat` 与 `--configure-only` 的过期表述
- CONTEXT 的「项目命令 id」术语按现役消费方改写
- 主实施计划与 decision-log 中被删 Tauri 后台任务命令名改为现役入口 start_game_creator_agent_background_task_for_session_at
2026-09-23 12:07:20 +08:00
k88936 c9bb5a93f0 文档:说明引用名归一化可能撞名,以及碰撞目前只在解析侧兜住
normalizeMentionName 的注释补上「归一化不保证名字唯一」:`hero v2` 与 `hero-v2` 折成同一个 token,候选菜单侧不做冲突检测、会显示同一个标签,碰撞由 buildContentFromPastedText 的「同名多候选一律按文本保留」兜住
同步 AGC 聊天素材引用功能说明的引用名空白不变量小节,写明这是已知残留
2026-09-23 11:55:53 +08:00
k88936 3391ecf7d4 加固:粘贴接管改为「真的插进正文之后」才生效,插入为空时放行默认粘贴
ResourceReferenceInput 的 PASTE_COMMAND 处理器原先先 preventDefault 再去插入:插入为空时这段文字既不进我们的插入、又被挡掉了默认导入。现在先做插入,只有插入确实发生了才 preventDefault 并返回 true,否则返回 false 交给 Lexical 的默认粘贴
$insertContentAtSelection 改为返回「插进去没有」:选区取不到、或每个 part 都没落下内容时返回 false
编辑器已在一次更新里时 editor.update 会把回调排队,这时按原口径先接管(ranSync 标记区分「回调已同步跑完」与「回调被排队」),避免误判成插入为空而放行默认粘贴造成双份插入
同步 AGC 聊天素材引用功能说明的粘贴解析小节
2026-09-23 11:55:40 +08:00
k88936 3f9c698a67 修复:粘贴解析跳过只有触发符的退化 token,不再错认正文里的裸 @
buildContentFromPastedText 组装候选时跳过 token 长度小于等于 1 的条目(引用名为空时 token 就是裸 `@` / `$`):边界规则会让它在正文里匹配到任何一处触发符,认下来等于把无关文字错认成一条引用
函数注释的保守规则从两条补成三条,写明空名字按「宁可不成 chip」的同一口径跳过
新增用例:空名字引用的 token 是 `@` 时不认领正文里的裸 `@`、其余 token 照常解析;去掉该跳过分支后用例变红
同步 AGC 聊天素材引用功能说明的粘贴解析小节
2026-09-23 11:54:09 +08:00
k88936 4e90465c45 修复:素材显示名在词干为空时回退 asset.id,不再产生空引用名
resourceDisplayName 在文件名去掉扩展名后为空(`.env` / `.gitignore` 这类整名就是扩展名的文件)时回退 asset.id,回退值同样过 normalizeMentionName;空名字会让 token 退化成只有触发符的裸 `@`,候选菜单里是空芯片、粘贴解析还会认领正文里任何一处裸 `@`
normalizeMentionName 的注释改为「只做归一化、不做兜底」,把兜底的落点写明在名字的来源侧
新增用例:`.env` 与 `config/.gitignore` 的显示名回退 asset.id、引用标签不再为空;把该回退去掉后用例变红
同步决策记录(校正「不做 resourceId 兜底」一条)、AGC 聊天素材引用功能说明与引用粘贴解析实施计划
2026-09-23 11:53:14 +08:00
k88936 f4294660d2 退役AGC项目对话斜杠命令与终端swarm chat入口:真实E2E、脚本与门禁清理
- 删除 scripts/agent-swarm-test-chat.mjs、agent-runtime-deterministic-playable-e2e.mjs、deterministic-lane-defense-provider.mjs
- 删除 user-input、supervisor-autonomous-playable-lane-defense、supervisor-swarm-autonomous-chat、static-isolated 与 collaboration-policy-mixed 套件
- 删除仅服务终端入口的交互式 CLI harness 管道与其进程清理、残留校验逻辑
- 清理 collaboration-assertions、evidence 模板与校验、self-test 中随之失效的混合与静态隔离断言及占位字段
- 移除 check-config.mjs 中钉住已删脚本、命令与套件的断言
- 删除 root 与应用 package.json 的 agc:test*、agc:chat、agc:swarm、各 *-real-e2e 与 test:chat* 脚本
- game-creator-config-wizard.mjs 改为从 channel-identity.mjs 取应用标识
2026-09-23 11:13:03 +08:00
k88936 6106b4e67c 退役AGC项目对话斜杠命令与终端swarm chat入口:应用与Rust实现删除
- 删除应用侧 /history 精确匹配分支与 reloadHistory、chatPromptPolish 的 / 前缀绕过、chatCommandMetadata、memoryCommands、projectSummaryConstants 命令清单
- 删除只服务退役摘要面板的 project-summary/*Summaries.ts 与 agentTrace.ts 及对应测试
- 删除 /sync-canvas-project、/read、/trace 草稿回填死链(agentPresentation.ts 与 Rust suggested_canvas_tool_call)
- 删除无人调用的 Tauri 命令 get_game_creation_agent_capabilities 与 get_limited_local_commands
- 删除 --swarm-chat 入口、SwarmChat 变体、src/swarm_cli.rs 与整个 swarm_cli/ 目录
- 收敛 agent/interaction.rs 至自然语言 steer 决策路径,删除交互内核整层
- 删除 SWARM_TURN_*_ERROR、print_runtime_response_stream_status 及其专属测试
- 更新 ChatMarkdownMessage、chatPromptPolish、rememberCommand 与 appSurface 用例,移除斜杠命令断言
2026-09-23 11:12:39 +08:00
k88936 530538842e 文档:收口退役AGC项目对话斜杠命令与终端swarm chat入口
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m20s
Project CI / Backend tests (pull_request) Failing after 16s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m8s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Failing after 7m22s
Project CI / Repository checks (pull_request) Failing after 12s
Project CI / Frontend tests (pull_request) Successful in 3m9s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 8m12s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m36s
Project CI / Native shell tests (pull_request) Successful in 7m13s
- 新增 ADR:记录斜杠命令与终端 swarm chat 入口整体退役的决策、保留项与影响边界
- 新增里程碑计划:登记删除范围、检查点、验证命令与未做边界
- 更新 decision-log:新增 2026-09-22 决策条目,清理被本次退役取代的历史命令条目
- 更新 pitfalls:移除已退役命令入口的现役表述
- 更新 CONTEXT.md 术语:把「斜杠命令」标为已退役说法
- 更新 docs/README.md:登记新 ADR
- 更新 AGC 主实施计划:删除全部「聊天输入 /<cmd>」能力条目与终端聊天入口承诺
- 更新 Agent Runtime V1.1 文档:删除终端 swarm chat 入口整节,控制面统一记为 --agent-* CLI
- 更新 DirectProject 里程碑计划:/history 入口表述改为显式重新加载对话
2026-09-23 11:10:55 +08:00
k88936 44148542f1 Merge remote-tracking branch 'origin/master' into feat/smart-paste
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m10s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m30s
Project CI / Backend tests (pull_request) Successful in 4m43s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 8m16s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 9m10s
Project CI / Frontend tests (pull_request) Successful in 2m23s
Project CI / Native shell tests (pull_request) Successful in 7m2s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m10s
Project CI / Repository checks (pull_request) Successful in 2m49s
# Conflicts:
#	docs/project-memory/shared-memory/decision-log.md
2026-09-23 11:04:46 +08:00
k88936 5e36b61534 修复:引用名内部空白统一折成 -,粘贴解析不再多插一枚芯片
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m21s
Project CI / Backend tests (pull_request) Failing after 11s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m53s
Project CI / Frontend tests (pull_request) Successful in 2m4s
Project CI / Repository checks (pull_request) Failing after 12s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m19s
Project CI / Native shell tests (pull_request) Successful in 5m13s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 6m46s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m16s
resourceReferences.ts 新增共享 normalizeMentionName:内部空白折 `-`、裁掉首尾,token 层幂等再折一次
素材显示名(resourceDisplayName)、Skill 名(目录读入与 toReference / mentionToken)、附件名(导入映射与 toReference / mentionToken)统一过这一份口径
resourceDisplayName 去掉 `|| asset.id` 兜底:引用名假定非空,不再回退 resourceId
前缀重叠 bug 由此收口:`@hero` 不再命中 `@hero-v2`(后一个字符不是空白),粘贴只剩正确的那一枚芯片
新增用例:素材 / Skill / 附件三类归一化口径、前缀重叠回归、Skill 目录名带空白、附件名带空白;把 normalizeMentionName 变异成恒等函数后新增用例全红
顺带修掉 referenceSourceProviders 用例里重命名遗留的 provider.match 断言与 skill provider 注释里的 match
同步 CONTEXT.md 术语、功能说明、实施计划与决策记录
2026-09-23 10:40:21 +08:00
k88936 072bf691d9 修复:粘贴解析注释里的 provider 成员名与实际 API 对齐
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
buildContentFromPastedText 的注释仍写着重命名前的 ReferenceProvider.candidates,改成 lookup
顺带写明候选来自调用方传入的引用清单,避免把「谁去查候选」误记进这个纯函数
2026-09-22 19:56:21 +08:00
k88936 14f11e77a2 修复:粘贴解析出的引用若解析不出来,退化成 token 文本而不是整条丢掉
$insertContentAtSelection 的引用分支补 else:referenceFromPart 返回 null 时插入该 part 的 token 文本
此前上游 buildContentFromPastedText 已把 token 摘走,插入端再丢一次就会让这段内容静默消失
新增用例:provider 认得出 token 但插入时已解析不出引用,断言正文保持 `看 @hero 一眼`、不产生芯片
变异验证:去掉该兜底后同一用例变红(正文变成 `看  一眼`),恢复即绿
2026-09-22 19:54:36 +08:00
k88936 9e50947d02 重构:引用输入区的选区兜底收敛成一个 helper
抽出 $selectionOrRootEnd():选区缺失或指向已被重建掉的节点时统一回落到草稿末尾
insertReferences / insertText / $insertContentAtSelection 三处重复的兜底判断改为调用它
行为不变:插件插入与粘贴插入仍走同一套「插到草稿末尾」的策略,只是不再各写一份
2026-09-22 19:53:42 +08:00
k88936 ec1f207021 重构:引用 provider 的 match / candidates 改名为 fuzzyLookup / lookup,删除 onPasteText
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m16s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m7s
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
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
match(query) 改名 fuzzyLookup(query):名字写明它是包含匹配 + 截断的模糊菜单查询
candidates() 改名 lookup():名字写明它是精确查找用的就绪全量候选,不模糊不截断
删除 onPasteText 补读钩子:粘贴只用此刻就绪的候选,不等待、不补读,也不为解析提前读盘
输入区与资源 / Skill provider 同步改名,粘贴接管路径改读 lookup()
provider 用例与粘贴集成用例同步:Skill 冷启动改为「保持字面,敲过 `$` 后可解析」
补 jsdom 缺的 Range.getBoundingClientRect 与 ResizeObserver 桩(候选菜单测量用,真实浏览器都有)
同步 ADR 修订、实施计划、功能说明与决策记录
2026-09-22 19:50:42 +08:00
k88936 6950e08506 功能:引用输入区支持粘贴解析 @显示名 / $名称
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m26s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m55s
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
新增 buildContentFromPastedText:粘贴文本按显示口径反解析,同名多候选与未命中 token 一律按文本保留
ReferenceProvider 增加可选 candidates 与 onPasteText:候选枚举保持纯函数,Skill 目录冷启动时补读一次
资源与 Skill provider 落地新缝,静默 provider(附件 / 运行画面区域)不参与粘贴解析
输入区在 CRITICAL 优先级接管 PASTE_COMMAND,仅解析出引用时插入并打 PASTE_TAG,一次 Ctrl+Z 整体回退
补充粘贴解析规则矩阵、provider 候选枚举与补读、输入区粘贴集成用例
同步 ADR 修订、实施计划、术语表、功能说明与决策记录
2026-09-22 19:06:19 +08:00
304 changed files with 9828 additions and 36999 deletions
+2 -6
View File
@@ -237,14 +237,10 @@ GENARRATIVE_ENABLE_IMAGE_EDITOR_AGENT_SIDEBAR="false"
# 官网客户端下载检测渠道:dev、release 或自定义渠道;修改后重启 API 服务。
# Windows/macOS 是系统维度,不填写 dev-win/dev-mac。
# 客户端埋点也复用该渠道:dev 对应 https://dev.genarrative.worldrelease 对应 https://www.genarrative.world。
# 埋点不接受其它渠道;本地 dev 且 GENARRATIVE_ENV 为 development(默认)/test/container 时允许 loopback 地址及可变端口。
GENARRATIVE_CLIENT_DOWNLOAD_CHANNEL="dev"
# 客户端埋点接收绑定的公开 origin,由 API Server 运行时读取;修改后重启服务。
# 必须与客户端登录地址一致,不带 /api、路径或尾部斜杠;未配置/非法时上传接口返回 503。
# 本地端口按实际启动结果填写(端口漂移后需同步),localhost 与 127.0.0.1 不可混用。
# dev 使用 https://dev.genarrative.worldrelease 使用 https://www.genarrative.world。
GENARRATIVE_AGC_ANALYTICS_ORIGIN="http://127.0.0.1:8082"
# Optional: official VikingDB credentials for regenerating build-tag similarities
# with the Python embedding script. The script auto-loads `.env.local` and uses
# the fixed `bge-large-zh` embedding model.
+21
View File
@@ -194,6 +194,14 @@ _Avoid_: 进度通知、快照轮询、第二套历史
把项目对话历史条目与运行态事件转换成消息气泡和工具卡片的读取期转换;不持久化,也不构成事实源。
_Avoid_: 投影缓存文件、已脱敏卡片库、第二套 reducer
**项目对话输入**:
AGC 项目对话的输入只有自然语言回合(含 `@` 素材引用与附件);需要动作时由 Runtime 工具与确认卡承接,不从输入文本解析控制词。
_Avoid_: 斜杠命令、聊天命令草稿、命令发现列表
**项目命令 id**:
AGC 运行期工具与项目权限策略使用的稳定标识(`GAME_CREATION_APP_COMMANDS``GameCreationAppPermission`),由 Rust 运行期策略校验与 App 权限审计 / 项目前置条件判定消费,不是用户输入语法。
_Avoid_: 把命令 id 当作可输入的聊天命令、为权限位补聊天入口
**引用候选**:
输入区可以命中的对象集合(`@` 素材、`$` Skill),由宿主按种类注入;输入区不判断候选属于哪一类。
_Avoid_: 输入区自己读项目清单或应用目录、把候选取值写死在组件里
@@ -210,6 +218,18 @@ _Avoid_: 给附件或运行画面区域造候选、为它们保留输入区内
引用在正文文本里的形态(`@显示名` / `$名称` / `@附件名`)及其反解析;出站与解析必须同一口径,token 前后各留一个空白。
_Avoid_: 出站与解析各写一套、在空白边界之外再补兼容别名、让解析依赖具体种类的字段
**引用名**:
引用自己的名字,同时就是它在正文里的 token(资源显示名、Skill 名、附件名);内部不允许出现空白,空白统一经 `normalizeMentionName` 折成 `-`
_Avoid_: 名字与 token 各存一份、靠补兼容别名或 `resourceId` 兜底来消化空白
**引用候选枚举**:
一种引用种类当前就绪的全部可引用对象,与候选菜单共用同一份集合;区别只在没有查询过滤和条数上限。
_Avoid_: 拿菜单查询当枚举、为粘贴另建一份候选清单
**引用粘贴解析**:
把粘贴进来的纯文本按引用文本语法反解析回正文引用;只有身份唯一且逐字确认的 token 才成为引用,其余按原文保留。
_Avoid_: 猜文件名或路径、为不确定的 token 挑一个候选、改写用户粘贴的其余文字
**引用输入区**:
只负责编辑与渲染引用的共享输入组件;候选、身份解析与文本语法都来自注入的 provider,它不持有项目清单、不访问后端。
_Avoid_: 输入区自己拉 Skill 目录、把选择器面板塞在输入区内部
@@ -240,6 +260,7 @@ _Avoid_: 待发送附件列表与正文芯片并存、提交时再拼一遍附
- **引用输入区** 由宿主注入的若干 **引用 provider** 组成;**引用候选** 与 **引用文本语法** 都来自 provider,输入区不判断引用种类。
- **引用选择器** 不属于 **引用输入区**:它自己拿数据,确认后只通过输入区的插入缝交付引用。
- 只有带触发符的 **引用 provider** 会产生候选;**静默 provider** 没有触发符,只能由外部插入或草稿回填进入正文。
- **引用粘贴解析** 与出站显示是同一条 **引用文本语法** 的两端;**静默 provider** 的 token`@附件名` / `@区域标签`)没有候选,因此粘贴时不重建。
- **附件芯片** 是本轮附件的唯一事实源;附件导入失败时不产生芯片。
## Example dialogue
+7
View File
@@ -193,7 +193,10 @@ export interface AdminDashboardRangePayload {
export interface AdminDashboardMetricsPayload {
generatedAssets: number;
/** 已对冲退还(生成失败 / 精选审核返还 / LLM Router 冲正)的净消耗泥点。 */
consumedMudPoints: number;
/** 同期退还泥点,用于核对「消耗 + 退还」的毛消耗口径。 */
refundedMudPoints: number;
totalRegisteredUsers: number;
newRegisteredUsers: number;
newUserPaymentConversion: AdminDashboardPaymentConversionPayload;
@@ -955,6 +958,8 @@ export interface AdminRechargeOrderEntryPayload {
productTitle: string;
productKind: string;
amountCents: number;
/** 真实支付金额(分):未支付 / 已关闭 / 已过期订单固定为 0,不能拿订单金额当实付。 */
paidAmountCents: number;
status: string;
paymentChannel: string;
paidAtMicros?: number | null;
@@ -994,6 +999,8 @@ export interface AdminUserDetailResponse {
phoneBound: boolean;
wechatBound: boolean;
historicalConsumedPoints: number;
/** 累计充值金额(分):读取失败或命中读取上限时为 null,前端按未知展示。 */
cumulativeRechargedCents?: number | null;
canReconcileConsumption: boolean;
wallet: AdminProfileWalletPayload;
rechargeOrders: AdminRechargeOrderEntryPayload[];
@@ -51,6 +51,7 @@ const detail: AdminUserDetailResponse = {
phoneBound: true,
wechatBound: true,
historicalConsumedPoints: 1234,
cumulativeRechargedCents: 128800,
canReconcileConsumption: true,
wallet,
rechargeOrders: [
@@ -62,6 +63,7 @@ const detail: AdminUserDetailResponse = {
productTitle: '60泥点',
productKind: 'points',
amountCents: 600,
paidAmountCents: 600,
status: 'paid',
paymentChannel: 'wechat_native',
paidAtMicros: 1_720_000_000_000_000,
@@ -124,7 +126,11 @@ test('用户查看按钮按内部 ID 查询并展示脱敏资料、余额与退
expect(screen.getByText('25', { selector: 'strong' })).toBeTruthy();
expect(screen.getByText('历史花费')).toBeTruthy();
expect(screen.getByText('1234', { selector: 'strong' })).toBeTruthy();
expect(screen.getByText('累计充值')).toBeTruthy();
expect(screen.getByText('¥1288.00')).toBeTruthy();
expect(screen.getByText('order-1')).toBeTruthy();
expect(screen.getByRole('columnheader', { name: '实付' })).toBeTruthy();
expect(screen.getByRole('columnheader', { name: '发放泥点' })).toBeTruthy();
await user.keyboard('{Escape}');
await waitFor(() =>
@@ -133,6 +139,29 @@ test('用户查看按钮按内部 ID 查询并展示脱敏资料、余额与退
await waitFor(() => expect(document.activeElement).toBe(trigger));
});
test('累计充值读取不到时展示未知,不用订单列表近似', async () => {
vi.mocked(getAdminUserDetail).mockResolvedValue({
...detail,
cumulativeRechargedCents: null,
});
const user = userEvent.setup();
render(
<AdminUserReferenceButton
token="admin-token"
userId="user-1"
onUnauthorized={vi.fn()}
/>,
);
await user.click(screen.getByRole('button', { name: '查看用户信息' }));
await screen.findByText('陶泥用户');
expect(screen.getByText('累计充值')).toBeTruthy();
expect(screen.getByText('累计充值').nextElementSibling?.textContent).toBe(
'未知',
);
});
test('只有陶泥号时按 publicUserCode 查询用户', async () => {
const user = userEvent.setup();
render(
@@ -364,6 +364,7 @@ export function AdminUserDetailDialog({
<th></th>
<th></th>
<th></th>
<th></th>
<th>退</th>
<th></th>
</tr>
@@ -377,11 +378,13 @@ export function AdminUserDetailDialog({
</span>
<small>{formatMicros(order.createdAtMicros)}</small>
</td>
<td>{order.productTitle || order.productId}</td>
<td>
{order.productTitle || order.productId}
<small> {order.pointsDelta} </small>
{order.paidAmountCents > 0
? formatMoney(order.paidAmountCents)
: '未支付'}
</td>
<td>{formatMoney(order.amountCents)}</td>
<td>{order.pointsDelta} </td>
<td>
{formatMoney(order.cumulativeSuccessRefundCents)}
<small> {order.unrecoveredPoints} </small>
@@ -435,6 +438,14 @@ function UserIdentityHeader({ detail }: { detail: AdminUserDetailResponse }) {
<dt></dt>
<dd>{detail.loginMethod || '-'}</dd>
</div>
<div>
<dt></dt>
<dd>
{typeof detail.cumulativeRechargedCents === 'number'
? formatMoney(detail.cumulativeRechargedCents)
: '未知'}
</dd>
</div>
<div>
<dt></dt>
<dd>
@@ -339,7 +339,10 @@ test.each([409, 503])(
await confirmWrite();
const message = await screen.findByRole('alert');
const feedback = message.parentElement!;
expect(document.activeElement).toBe(feedback);
// 聚焦发生在 React passive effect 里(AdminAgcTemplatesPage 的 feedback 聚焦 useEffect),
// 而 findByRole 在 alert 节点一挂上就返回,可能早于该 effect 执行;这里等聚焦落地,
// 避免在 CI 负载下抢跑。断言口径不变:焦点最终必须落在提示区而不是弹窗面板。
await waitFor(() => expect(document.activeElement).toBe(feedback));
expect(feedback.tabIndex).toBe(-1);
expect(focus).toHaveBeenCalledWith({ preventScroll: true });
expect(viewport.scrollTop).toBe(20);
@@ -34,6 +34,7 @@ const dashboardResponse: AdminDashboardResponse = {
metrics: {
generatedAssets: 12,
consumedMudPoints: 88,
refundedMudPoints: 24,
totalRegisteredUsers: 1200,
newRegisteredUsers: 16,
newUserPaymentConversion: {
@@ -105,6 +106,8 @@ test('Dashboard 默认加载今日指标并支持运营汇总页签', async () =
expect(await screen.findByText('总计数据')).toBeTruthy();
expect(screen.getByText('时段数据')).toBeTruthy();
expect(screen.getByText('本日生产素材数')).toBeTruthy();
expect(screen.getByText('本日消耗泥点数')).toBeTruthy();
expect(screen.getByText('本日退还泥点数')).toBeTruthy();
expect(screen.getByText('总注册用户')).toBeTruthy();
expect(screen.getByText('本日新增用户数')).toBeTruthy();
expect(screen.getByText('新增用户转化与留存')).toBeTruthy();
@@ -128,6 +128,12 @@ export function AdminDashboardPage({
value: metrics?.consumedMudPoints ?? 0,
unit: '泥点',
},
{
id: 'refunded-mud-points',
label: `${rangePrefix(granularity)}退还泥点数`,
value: metrics?.refundedMudPoints ?? 0,
unit: '泥点',
},
{
id: 'new-registered-users',
label: `${rangePrefix(granularity)}新增用户数`,
@@ -64,6 +64,7 @@ const baseOrder: AdminRechargeOrderEntryPayload = {
productTitle: '60泥点',
productKind: 'points',
amountCents: 600,
paidAmountCents: 600,
status: 'paid',
paymentChannel: 'wechat_native',
paidAtMicros: 1_720_000_000_000_000,
@@ -129,6 +130,40 @@ beforeEach(() => {
);
});
test('未支付订单不显示实付金额,发放泥点单独成列', async () => {
vi.mocked(listAdminRechargeOrders).mockResolvedValue({
entries: [
{
...baseOrder,
orderId: 'order-pending',
status: 'pending',
paidAtMicros: null,
paidAmountCents: 0,
pointsDelta: 0,
},
{ ...baseOrder, orderId: 'order-paid' },
],
});
renderPage();
expect(
await screen.findByRole('columnheader', { name: '实付' }),
).toBeTruthy();
expect(screen.getByRole('columnheader', { name: '发放泥点' })).toBeTruthy();
const unpaidRow = (await screen.findByText('order-pending')).closest(
'tr',
) as HTMLElement;
const unpaidCells = within(unpaidRow).getAllByRole('cell');
expect(unpaidCells[3]?.textContent).toContain('未支付');
expect(unpaidCells[4]?.textContent).toBe('0 泥点');
const paidRow = screen.getByText('order-paid').closest('tr') as HTMLElement;
const paidCells = within(paidRow).getAllByRole('cell');
expect(paidCells[3]?.textContent).toBe('¥6.00');
expect(paidCells[4]?.textContent).toBe('60 泥点');
});
test('充值订单查询传递全部筛选字段', async () => {
const user = userEvent.setup();
renderPage();
@@ -658,7 +658,8 @@ export function AdminRechargeOrderPage({
<th></th>
<th></th>
<th></th>
<th> / </th>
<th></th>
<th></th>
<th>退</th>
<th></th>
<th></th>
@@ -715,7 +716,7 @@ export function AdminRechargeOrderPage({
<div className="admin-refund-summary-grid">
<Metric
label="订单实付"
value={formatMoney(refundOrder.amountCents)}
value={formatMoney(refundOrder.paidAmountCents)}
/>
<Metric
label="累计已退"
@@ -1118,8 +1119,13 @@ function RechargeOrderRow({
</small>
</td>
<td>
<strong>{formatMoney(order.amountCents)}</strong>
<small> {order.pointsDelta} </small>
<strong>{formatOrderPaidAmount(order)}</strong>
{order.paidAmountCents > 0 ? null : (
<small> {formatMoney(order.amountCents)}</small>
)}
</td>
<td>
<span>{order.pointsDelta} </span>
</td>
<td>
<span> {formatMoney(order.cumulativeSuccessRefundCents)}</span>
@@ -1312,6 +1318,13 @@ function formatMoney(cents: number) {
return `¥${(cents / 100).toFixed(2)}`;
}
/** 实付只属于真正支付过的订单:未支付 / 已关闭 / 已过期订单显示“未支付”。 */
function formatOrderPaidAmount(order: AdminRechargeOrderEntryPayload) {
return order.paidAmountCents > 0
? formatMoney(order.paidAmountCents)
: '未支付';
}
function formatCentsInput(cents: number) {
return (cents / 100).toFixed(2);
}
@@ -217,7 +217,7 @@ export function AdminRedeemCodePage({
<div className="admin-form-row">
<label className="admin-field">
<span></span>
<span></span>
<input
min={1}
step={1}
@@ -319,7 +319,7 @@ export function AdminRedeemCodePage({
<thead>
<tr>
<th>Code</th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
@@ -337,7 +337,7 @@ export function AdminRedeemCodePage({
</button>
<small>{redeemModeLabel(entry.mode)}</small>
</td>
<td>{entry.rewardPoints}</td>
<td>{entry.rewardPoints} </td>
<td>
<span
className={`admin-status ${redeemValidityClass(entry)}`}
-10
View File
@@ -15,20 +15,10 @@
"skill-pack:test": "node --test scripts/check-skill-pack.test.mjs",
"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",
"swarm": "node scripts/run-cli-with-config.mjs --swarm-chat",
"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",
"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",
"agent-runtime:collaboration-policy-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-collaboration-policy-mixed-recovery",
"agent-runtime:mixed-swarm-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-static-isolated-autonomous-chat",
"agent-runtime:supervisor-swarm-autonomous-chat-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-autonomous-chat",
"agent-runtime:supervisor-autonomous-playable-lane-defense-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-autonomous-playable-lane-defense",
"agent-runtime:supervisor-autonomous-playable-lane-defense-deterministic-e2e": "node scripts/agent-runtime-deterministic-playable-e2e.mjs",
"agent-runtime:supervisor-autonomous-playable-lane-defense-deterministic-self-test": "node scripts/agent-runtime-deterministic-playable-e2e.mjs --self-test",
"agent-runtime:supervisor-swarm-transient-retry-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-transient-retry",
"agent-runtime:supervisor-swarm-final-reply-transient-retry-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-final-reply-transient-retry",
"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",
File diff suppressed because it is too large Load Diff
@@ -1,69 +1,8 @@
import { createHash } from '../dependencies.mjs';
import { isPlainObject } from '../harness/config.mjs';
import {
interactiveCliOutput,
writeInteractiveCliLine,
} from '../harness/process.mjs';
import {
shutdownWaiters,
state,
userInputAnswerText,
} from '../runtime-state.mjs';
import { shutdownWaiters, state } from '../runtime-state.mjs';
import { disposableProjectPathVariants } from './runtime.mjs';
export async function answerRemainingInteractiveQuestions(session) {
let answeredPromptCount = 1;
const deadline = Date.now() + 60_000;
while (Date.now() < deadline) {
const output = interactiveCliOutput(session);
if (output.includes(`[\u5df2\u56de\u7b54] ${state.userInput.requestId}`))
return;
const promptCount = output.split('或直接输入其他答案:').length - 1;
while (answeredPromptCount < promptCount && answeredPromptCount < 3) {
writeInteractiveCliLine(session, userInputAnswerText);
answeredPromptCount += 1;
}
if (output.includes('[待确认]')) {
throw codedError('user-input-unexpected-tool-confirmation');
}
if (session.closed) throw codedError('user-input-cli-closed-before-answer');
await sleep(100);
}
throw codedError('user-input-answer-timeout');
}
export function parseSingleSwarmTurnReport(output, codePrefix) {
const reportLines = output
.split(/\r?\n/u)
.filter((line) => line.startsWith('[turn.report] '));
assert(reportLines.length === 1, `${codePrefix}-turn-report-count-invalid`);
const report = JSON.parse(reportLines[0].slice('[turn.report] '.length));
assert(
isPlainObject(report) &&
JSON.stringify(Object.keys(report).sort()) ===
JSON.stringify(
[
'schemaVersion',
'outcome',
'parentAgentId',
'sessionId',
'parentRunId',
'runtimeCount',
'busyRuntimeCount',
'pendingTaskCount',
'runningTaskCount',
'waitingForConfirmationCount',
'waitingForUserInputCount',
'newAssistantMessageCount',
'finalReplyChars',
'reconciliationAgentCount',
].sort(),
),
`${codePrefix}-turn-report-shape-invalid`,
);
return report;
}
export function isFailedTask(task) {
return (
['failed', 'cancelled', 'budget-exhausted'].includes(task.status) ||
@@ -2419,24 +2419,6 @@ export function finalMessageId(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) {
const fingerprint = createHash('sha256')
.update(`${agentId}\n${sessionId}\n${runId}\n${source}`)
File diff suppressed because it is too large Load Diff
@@ -37,26 +37,16 @@ import {
state,
steerRunnerKillAppDataSentinelFileName,
steerRunnerKillAppDataSentinelSchema,
supervisorAutonomousPlayableAppDataSentinelFileName,
supervisorAutonomousPlayableAppDataSentinelSchema,
supervisorSwarmAppDataSentinelFileName,
supervisorSwarmAppDataSentinelSchema,
supervisorSwarmAutonomousChatAppDataSentinelFileName,
supervisorSwarmAutonomousChatAppDataSentinelSchema,
supervisorSwarmCollaborationPolicyAppDataSentinelFileName,
supervisorSwarmCollaborationPolicyAppDataSentinelSchema,
supervisorSwarmFinalReplyTransientRetryAppDataSentinelFileName,
supervisorSwarmFinalReplyTransientRetryAppDataSentinelSchema,
supervisorSwarmRequiredAgentIds,
supervisorSwarmStaticIsolatedAutonomousChatAppDataSentinelFileName,
supervisorSwarmStaticIsolatedAutonomousChatAppDataSentinelSchema,
supervisorSwarmToolPlanHandoffRunnerKillAppDataSentinelFileName,
supervisorSwarmToolPlanHandoffRunnerKillAppDataSentinelSchema,
supervisorSwarmTransientRetryAppDataSentinelFileName,
supervisorSwarmTransientRetryAppDataSentinelSchema,
supervisorSwarmTransientRetryTargetAgentId,
userInputAppDataSentinelFileName,
userInputAppDataSentinelSchema,
webSearchAppDataSentinelFileName,
webSearchAppDataSentinelSchema,
windowsProcessHandleHelperSource,
@@ -68,21 +58,14 @@ import { isProjectSkillSuite } from '../suites/project-skill.mjs';
import { isResponseStreamSuite } from '../suites/response-stream.mjs';
import { isScopedAgentsSuite } from '../suites/scoped-agents.mjs';
import { isSteerRunnerKillSuite } from '../suites/steer-runner-kill.mjs';
import { isSupervisorAutonomousPlayableLaneDefenseSuite } from '../suites/supervisor-autonomous-playable.mjs';
import {
isSupervisorSwarmAutonomousChatSuite,
isSupervisorSwarmCollaborationPolicyMixedRecoverySuite,
isSupervisorSwarmFinalReplyTransientRetrySuite,
isSupervisorSwarmInitialTransientRetrySuite,
isSupervisorSwarmInteractiveChatSuite,
isSupervisorSwarmMixedHarnessSuite,
isSupervisorSwarmStaticIsolatedAutonomousChatSuite,
isSupervisorSwarmSuite,
isSupervisorSwarmToolPlanHandoffRunnerKillSuite,
isSupervisorSwarmTransientRetrySuite,
rebuildSupervisorSwarmTranscriptScanner,
} from '../suites/supervisor-swarm.mjs';
import { isUserInputRuntimeSuite } from '../suites/user-input.mjs';
import {
isWebSearchSuite,
sameEffectiveAgentLlmWithoutWebSearch,
@@ -100,19 +83,10 @@ import { appendBounded, runProcess } from './process.mjs';
import { decodeUtf8Fatal, isIsolatedRunnerSuite } from './reporting.mjs';
import { killRunnerOnce, readRunnerStatus, runnerBootId } from './runtime.mjs';
const platformSessionFixtureEnv = 'GENARRATIVE_AGC_PLATFORM_SESSION_FIXTURE';
const platformSessionFixtureMaxBytes = 16 * 1024;
const isolatedPlatformSessionFixtureName =
'.deterministic-platform-session.json';
const platformSessionFixtureSchema =
'genarrative-agc-platform-session-fixture.v1';
export function isolatedSuiteProtectsSourceAppData() {
return (
isSupervisorSwarmTransientRetrySuite() ||
isSupervisorSwarmToolPlanHandoffRunnerKillSuite() ||
isSupervisorAutonomousPlayableLaneDefenseSuite() ||
isSupervisorSwarmInteractiveChatSuite()
isSupervisorSwarmToolPlanHandoffRunnerKillSuite()
);
}
@@ -120,27 +94,6 @@ export function isolatedSuiteUsesSiblingAppData() {
return isWebSearchSuite() || isolatedSuiteProtectsSourceAppData();
}
export function sameSupervisorPlayableProviderBinding(left, right) {
const scalarFields = [
'providerAgentMode',
'providerModel',
'providerApiKind',
'providerReasoningEffort',
'providerBaseUrlSha256',
];
return (
left != null &&
right != null &&
scalarFields.every((field) => left[field] === right[field]) &&
Array.isArray(left.boundAgentIds) &&
Array.isArray(right.boundAgentIds) &&
left.boundAgentIds.length === right.boundAgentIds.length &&
left.boundAgentIds.every(
(agentId, index) => agentId === right.boundAgentIds[index],
)
);
}
export function isolatedSuiteAppDataProfile() {
if (isSteerRunnerKillSuite()) {
return {
@@ -174,14 +127,6 @@ export function isolatedSuiteAppDataProfile() {
codePrefix: 'context-compaction-appdata',
};
}
if (isUserInputRuntimeSuite()) {
return {
prefix: '.agent-runtime-real-e2e-user-input-',
sentinelName: userInputAppDataSentinelFileName,
sentinelSchema: userInputAppDataSentinelSchema,
codePrefix: 'user-input-appdata',
};
}
if (isScopedAgentsSuite()) {
return {
prefix: '.agent-runtime-real-e2e-scoped-agents-',
@@ -236,41 +181,6 @@ export function isolatedSuiteAppDataProfile() {
codePrefix: 'supervisor-swarm-transient-retry-appdata',
};
}
if (isSupervisorAutonomousPlayableLaneDefenseSuite()) {
return {
prefix: '.agent-runtime-real-e2e-supervisor-autonomous-playable-',
sentinelName: supervisorAutonomousPlayableAppDataSentinelFileName,
sentinelSchema: supervisorAutonomousPlayableAppDataSentinelSchema,
codePrefix: 'supervisor-autonomous-playable-appdata',
};
}
if (isSupervisorSwarmAutonomousChatSuite()) {
return {
prefix: '.agent-runtime-real-e2e-supervisor-swarm-autonomous-chat-',
sentinelName: supervisorSwarmAutonomousChatAppDataSentinelFileName,
sentinelSchema: supervisorSwarmAutonomousChatAppDataSentinelSchema,
codePrefix: 'supervisor-swarm-autonomous-chat-appdata',
};
}
if (isSupervisorSwarmStaticIsolatedAutonomousChatSuite()) {
return {
prefix:
'.agent-runtime-real-e2e-supervisor-swarm-static-isolated-autonomous-chat-',
sentinelName:
supervisorSwarmStaticIsolatedAutonomousChatAppDataSentinelFileName,
sentinelSchema:
supervisorSwarmStaticIsolatedAutonomousChatAppDataSentinelSchema,
codePrefix: 'supervisor-swarm-static-isolated-autonomous-chat-appdata',
};
}
if (isSupervisorSwarmCollaborationPolicyMixedRecoverySuite()) {
return {
prefix: '.agent-runtime-real-e2e-supervisor-swarm-collaboration-policy-',
sentinelName: supervisorSwarmCollaborationPolicyAppDataSentinelFileName,
sentinelSchema: supervisorSwarmCollaborationPolicyAppDataSentinelSchema,
codePrefix: 'supervisor-swarm-collaboration-policy-appdata',
};
}
if (isSupervisorSwarmSuite()) {
return {
prefix: '.agent-runtime-real-e2e-supervisor-swarm-',
@@ -505,120 +415,6 @@ export async function verifySourceAppDataDirectoryUntouched() {
state.isolatedRunner.sourceAppDataDirectoryUntouched = true;
}
async function readPlatformSessionFixtureForIsolatedSuite(sourceConfigDir) {
const rawPath = process.env[platformSessionFixtureEnv];
assert(
isNonEmptyString(rawPath) && path.isAbsolute(rawPath),
'supervisor-autonomous-playable-platform-session-fixture-missing',
);
const sourceRealPath = await fs.realpath(sourceConfigDir);
const requestedPath = path.resolve(rawPath);
const requestedMetadata = await fs.lstat(requestedPath).catch((error) => {
if (error?.code === 'ENOENT') return null;
throw error;
});
assert(
requestedMetadata?.isFile() && !requestedMetadata.isSymbolicLink(),
'supervisor-autonomous-playable-platform-session-fixture-not-regular',
);
assert(
requestedMetadata.size <= platformSessionFixtureMaxBytes,
'supervisor-autonomous-playable-platform-session-fixture-too-large',
);
const realPath = await fs.realpath(requestedPath);
assert(
isPathInside(sourceRealPath, realPath),
'supervisor-autonomous-playable-platform-session-fixture-outside-config',
);
const bytes = await fs.readFile(realPath);
assert(
bytes.length <= platformSessionFixtureMaxBytes,
'supervisor-autonomous-playable-platform-session-fixture-too-large',
);
let fixture;
try {
fixture = JSON.parse(
decodeUtf8Fatal(bytes, 'platform-session-fixture-invalid-utf8'),
);
} catch (error) {
throw codedError(
'supervisor-autonomous-playable-platform-session-fixture-invalid',
error,
);
}
const expectedKeys = [
'schemaVersion',
'userId',
'accessToken',
'apiBaseUrl',
'generation',
];
assert(
isPlainObject(fixture) &&
JSON.stringify(Object.keys(fixture).sort()) ===
JSON.stringify([...expectedKeys].sort()) &&
fixture.schemaVersion === platformSessionFixtureSchema &&
isNonEmptyString(fixture.userId) &&
isNonEmptyString(fixture.accessToken) &&
isNonEmptyString(fixture.apiBaseUrl) &&
Number.isSafeInteger(fixture.generation) &&
fixture.generation > 0,
'supervisor-autonomous-playable-platform-session-fixture-invalid',
);
return {
sourcePath: realPath,
bytes,
fixture,
sha256: createHash('sha256').update(bytes).digest('hex'),
};
}
async function installPlatformSessionFixtureIntoIsolatedAppData(
sourceConfigDir,
appDataDir,
) {
if (!isSupervisorAutonomousPlayableLaneDefenseSuite()) return;
const source =
await readPlatformSessionFixtureForIsolatedSuite(sourceConfigDir);
const isolatedPath = path.join(
appDataDir,
isolatedPlatformSessionFixtureName,
);
await fs.copyFile(
source.sourcePath,
isolatedPath,
fsConstants.COPYFILE_EXCL | fsConstants.COPYFILE_FICLONE,
);
await fs.chmod(isolatedPath, 0o600).catch(() => {});
const isolatedMetadata = await fs.lstat(isolatedPath);
assert(
isolatedMetadata.isFile() &&
!isolatedMetadata.isSymbolicLink() &&
isolatedMetadata.size === source.bytes.length,
'supervisor-autonomous-playable-platform-session-fixture-copy-invalid',
);
const isolatedBytes = await fs.readFile(isolatedPath);
assert(
createHash('sha256').update(isolatedBytes).digest('hex') === source.sha256,
'supervisor-autonomous-playable-platform-session-fixture-copy-mismatch',
);
state.isolatedRunner.platformSessionFixtureSourcePath = source.sourcePath;
state.isolatedRunner.platformSessionFixturePath = isolatedPath;
state.isolatedRunner.platformSessionFixtureSha256 = source.sha256;
state.isolatedRunner.platformSessionFixturePreviousEnv =
Object.prototype.hasOwnProperty.call(process.env, platformSessionFixtureEnv)
? process.env[platformSessionFixtureEnv]
: undefined;
process.env[platformSessionFixtureEnv] = isolatedPath;
state.formalConfigPathTranscriptScanner?.addSecrets(
absolutePathVariants(source.sourcePath, isolatedPath),
);
const previousLeakCount = state.transcriptScanner?.count ?? 0;
state.secrets = [...new Set([...state.secrets, source.fixture.accessToken])];
rebuildSupervisorSwarmTranscriptScanner();
state.transcriptScanner.count = previousLeakCount;
}
export async function prepareIsolatedSuiteAppData({
streamAgentId = null,
webSearchAgentId = null,
@@ -773,8 +569,7 @@ export async function prepareIsolatedSuiteAppData({
isScopedAgentsSuite() ||
isProjectSkillSuite() ||
isParallelReadSuite() ||
isSupervisorSwarmSuite() ||
isSupervisorAutonomousPlayableLaneDefenseSuite()
isSupervisorSwarmSuite()
? 'private-copy'
: 'hardlink';
try {
@@ -891,10 +686,6 @@ export async function prepareIsolatedSuiteAppData({
state.secrets = [...suiteSecrets];
rebuildSupervisorSwarmTranscriptScanner();
state.transcriptScanner.count = previousLeakCount;
await installPlatformSessionFixtureIntoIsolatedAppData(
sourceConfigDir,
appDataDir,
);
const unexpectedEndpoint = await fs
.lstat(path.join(appDataDir, runnerEndpointFileName))
.catch((error) => {
@@ -903,46 +694,6 @@ export async function prepareIsolatedSuiteAppData({
});
assert(!unexpectedEndpoint, `${profile.codePrefix}-endpoint-preexisted`);
state.runtimeConfigDir = appDataDir;
if (isSupervisorAutonomousPlayableLaneDefenseSuite()) {
const isolatedConfig = await loadConfig(appDataDir);
const expectedBinding = state.config.providerBinding;
const agentIds = [projectSupervisorAgentId];
const bindings = agentIds.map((agentId) => {
const effective = effectiveAgentLlmConfig(isolatedConfig.config, agentId);
assert(
['apiKey', 'baseUrl', 'model', 'apiKind', 'reasoningEffort'].every(
(key) => isNonEmptyString(effective[key]),
),
'supervisor-autonomous-playable-effective-provider-incomplete',
);
return {
providerModel: effective.model.trim(),
providerApiKind: effective.apiKind.trim(),
providerReasoningEffort: effective.reasoningEffort.trim(),
providerBaseUrlSha256: hashValue(effective.baseUrl.trim()),
};
});
const effectiveBinding = {
...bindings[0],
boundAgentIds: [...agentIds].sort(),
};
assert(
expectedBinding &&
bindings.every(
(binding) => JSON.stringify(binding) === JSON.stringify(bindings[0]),
) &&
sameSupervisorPlayableProviderBinding(
effectiveBinding,
expectedBinding,
),
'supervisor-autonomous-playable-effective-provider-binding-mismatch',
);
state.supervisorAutonomousPlayable.expectedProviderBinding = {
...expectedBinding,
};
state.supervisorAutonomousPlayable.effectiveProviderBinding =
effectiveBinding;
}
if (streamAgentId) {
const isolatedConfig = await loadConfig(appDataDir);
const isolatedEffective = effectiveAgentLlmConfig(
@@ -981,22 +732,6 @@ export async function prepareIsolatedSuiteAppData({
);
state.webSearch.effectiveEnabled = true;
}
if (isUserInputRuntimeSuite()) {
const isolatedConfig = await loadConfig(appDataDir);
const isolatedEffective = effectiveAgentLlmConfig(
isolatedConfig.config,
projectSupervisorAgentId,
);
assert(
isolatedEffective.model === 'gpt-5.5' &&
['apiKey', 'baseUrl', 'model'].every(
(key) =>
typeof isolatedEffective[key] === 'string' &&
isolatedEffective[key].trim().length > 0,
),
'user-input-effective-gpt-5-5-config-invalid',
);
}
if (isScopedAgentsSuite()) {
const isolatedConfig = await loadConfig(appDataDir);
const isolatedEffective = effectiveAgentLlmConfig(
@@ -1073,9 +808,8 @@ export async function prepareIsolatedSuiteAppData({
effective.requestTimeoutMs > 0 &&
Number.isSafeInteger(effective.maxRetries) &&
effective.maxRetries >=
(isSupervisorSwarmInteractiveChatSuite() ||
(isSupervisorSwarmTransientRetrySuite() &&
agentId !== supervisorSwarmTransientRetryTargetAgentId)
agentId !== supervisorSwarmTransientRetryTargetAgentId
? 0
: 1) &&
effective.maxRetries <= 3 &&
@@ -1095,24 +829,6 @@ export async function prepareIsolatedSuiteAppData({
{ llm: isolatedConfig.config.llm },
'',
);
if (isSupervisorSwarmMixedHarnessSuite()) {
assert(
globalEffective.model === 'gpt-5.5' &&
globalEffective.apiKind === 'openai_chat' &&
isNonEmptyString(globalEffective.reasoningEffort) &&
Number.isSafeInteger(globalEffective.requestTimeoutMs) &&
globalEffective.requestTimeoutMs > 0 &&
Number.isSafeInteger(globalEffective.maxRetries) &&
globalEffective.maxRetries >= 0 &&
globalEffective.maxRetries <= 3 &&
Number.isSafeInteger(globalEffective.retryBackoffMs) &&
globalEffective.retryBackoffMs > 0 &&
['apiKey', 'baseUrl', 'model'].every((key) =>
isNonEmptyString(globalEffective[key]),
),
'supervisor-swarm-mixed-default-provider-policy-invalid',
);
}
const configuredAgentIds = Object.keys(
isPlainObject(isolatedConfig.config.agentLlm)
? isolatedConfig.config.agentLlm
@@ -2000,31 +1716,6 @@ export async function verifyIsolatedSuiteConfigLinksUnchanged() {
'isolated-source-config-changed-during-suite',
);
}
const fixture = state.isolatedRunner;
if (
fixture.platformSessionFixtureSourcePath &&
fixture.platformSessionFixturePath &&
fixture.platformSessionFixtureSha256
) {
const [sourceMetadata, isolatedMetadata, sourceBytes, isolatedBytes] =
await Promise.all([
fs.lstat(fixture.platformSessionFixtureSourcePath),
fs.lstat(fixture.platformSessionFixturePath),
fs.readFile(fixture.platformSessionFixtureSourcePath),
fs.readFile(fixture.platformSessionFixturePath),
]);
assert(
sourceMetadata.isFile() &&
!sourceMetadata.isSymbolicLink() &&
isolatedMetadata.isFile() &&
!isolatedMetadata.isSymbolicLink() &&
createHash('sha256').update(sourceBytes).digest('hex') ===
fixture.platformSessionFixtureSha256 &&
createHash('sha256').update(isolatedBytes).digest('hex') ===
fixture.platformSessionFixtureSha256,
'supervisor-autonomous-playable-platform-session-fixture-changed',
);
}
}
export async function verifySourceConfigLinkCountsRestored() {
@@ -2064,22 +1755,7 @@ export async function removeIsolatedSuiteAppData() {
} catch (error) {
ownershipError = error;
}
try {
await fs.rm(appDataDir, { recursive: true, force: false });
} finally {
if (state.isolatedRunner.platformSessionFixtureSourcePath) {
const previous = state.isolatedRunner.platformSessionFixturePreviousEnv;
if (previous === undefined) {
delete process.env[platformSessionFixtureEnv];
} else {
process.env[platformSessionFixtureEnv] = previous;
}
}
state.isolatedRunner.platformSessionFixturePath = null;
state.isolatedRunner.platformSessionFixtureSourcePath = null;
state.isolatedRunner.platformSessionFixtureSha256 = null;
state.isolatedRunner.platformSessionFixturePreviousEnv = undefined;
}
await fs.rm(appDataDir, { recursive: true, force: false });
state.runtimeConfigDir = state.options.configDir;
try {
await verifySourceConfigLinkCountsRestored();
@@ -13,15 +13,10 @@ import {
responseStreamSuite,
scopedAgentsSuite,
steerRunnerKillSuite,
supervisorAutonomousPlayableLaneDefenseSuite,
supervisorSwarmAutonomousChatSuite,
supervisorSwarmCollaborationPolicyMixedRecoverySuite,
supervisorSwarmFinalReplyTransientRetrySuite,
supervisorSwarmStaticIsolatedAutonomousChatSuite,
supervisorSwarmSuite,
supervisorSwarmToolPlanHandoffRunnerKillSuite,
supervisorSwarmTransientRetrySuite,
userInputRuntimeSuite,
webSearchSuite,
} from '../runtime-state.mjs';
import { collectApiKeys, isPathInside } from './io.mjs';
@@ -58,7 +53,6 @@ export function parseArguments(args) {
suite === responseStreamSuite ||
suite === webSearchSuite ||
suite === contextCompactionSuite ||
suite === userInputRuntimeSuite ||
suite === scopedAgentsSuite ||
suite === projectSkillSuite ||
suite === parallelReadSuite ||
@@ -66,10 +60,6 @@ export function parseArguments(args) {
suite === supervisorSwarmTransientRetrySuite ||
suite === supervisorSwarmFinalReplyTransientRetrySuite ||
suite === supervisorSwarmToolPlanHandoffRunnerKillSuite ||
suite === supervisorSwarmAutonomousChatSuite ||
suite === supervisorAutonomousPlayableLaneDefenseSuite ||
suite === supervisorSwarmStaticIsolatedAutonomousChatSuite ||
suite === supervisorSwarmCollaborationPolicyMixedRecoverySuite ||
suite === steerRunnerKillSuite ||
processSessionSuites.has(suite),
'unsupported-suite',
@@ -2,7 +2,6 @@ import {
assert,
codedError,
hashValue,
sleep,
throwIfShutdownRequested,
} from '../assertions/core.mjs';
import { fs, path, spawn, withLoopbackNoProxy } from '../dependencies.mjs';
@@ -13,14 +12,9 @@ import {
manifestPath,
state,
} from '../runtime-state.mjs';
import {
isSupervisorSwarmTransientRetrySuite,
recordSupervisorSwarmChatSessionFailureDiagnostic,
} from '../suites/supervisor-swarm.mjs';
import { isSupervisorSwarmTransientRetrySuite } 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(
@@ -84,8 +78,8 @@ export function buildCliChildEnvironment() {
NO_COLOR: '1',
RUST_BACKTRACE: '0',
};
// The deterministic playable suite copies its account fixture into the
// sibling isolated AppData directory. Set the path explicitly here so
// The isolated runner copies its account fixture into the sibling isolated
// AppData directory. Set the path explicitly here so
// every CLI and the Runner it launches use the isolated copy, even if the
// parent harness environment was restored or changed after setup.
if (state.isolatedRunner.platformSessionFixturePath) {
@@ -144,197 +138,6 @@ export function codedProcessError(code, details) {
return error;
}
export function startInteractiveCli(args) {
assert(Boolean(state.cliBinary), 'interactive-cli-binary-not-ready');
assert(Boolean(state.runtimeConfigDir), 'interactive-config-dir-not-ready');
if (
isIsolatedRunnerSuite() &&
state.options?.configDir &&
path.resolve(state.runtimeConfigDir) ===
path.resolve(state.options.configDir)
) {
state.isolatedRunner.sourceConfigCliCallCount += 1;
}
const child = spawn(
state.cliBinary,
[...args, '--config-dir', state.runtimeConfigDir],
{
cwd: appRoot,
env: buildCliChildEnvironment(),
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.once('close', (code, signal) => {
activeCommandChildren.delete(child);
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);
});
return session;
}
export function interactiveCliOutput(session) {
return `${session.stdout.toString('utf8')}\n${session.stderr.toString('utf8')}`;
}
export function writeInteractiveCliLine(session, line) {
assert(!session.closed, 'interactive-cli-already-closed');
assert(session.child.stdin.writable, 'interactive-cli-stdin-not-writable');
session.child.stdin.write(`${line}\n`);
}
export async function waitForInteractiveCliOutput(
session,
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.exited && !allowAfterProcessExit) {
if (session === state.supervisorSwarmCliSession) {
recordSupervisorSwarmChatSessionFailureDiagnostic(session);
}
throw codedError(`${code}-cli-exited`);
}
if (session.stdioClosed) {
if (session === state.supervisorSwarmCliSession) {
recordSupervisorSwarmChatSessionFailureDiagnostic(session);
}
throw codedError(`${code}-cli-stdio-closed`);
}
await sleep(50);
}
throw codedError(code);
}
export async function waitForInteractiveCliExit(session, timeoutMs) {
const result = await Promise.race([
session.exitPromise,
sleep(timeoutMs).then(() => null),
]);
if (!result) throw codedError('interactive-cli-exit-timeout');
if (result.error) throw codedError('interactive-cli-process-error');
assert(
result.code === 0 && result.signal === null,
'interactive-cli-exit-invalid',
);
return result;
}
export async function closeInteractiveCli(session) {
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.exitPromise,
sleep(3_000).then(() => null),
]);
if (!result && !session.exited) {
session.child.kill('SIGTERM');
result = await Promise.race([
session.exitPromise,
sleep(2_000).then(() => null),
]);
}
if (!result && !session.exited) {
session.child.kill('SIGKILL');
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(
program,
args,
@@ -406,214 +209,6 @@ 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
@@ -28,12 +28,10 @@ import {
isGoalRuntimeSuite,
} from '../suites/goal.mjs';
import { isResponseStreamSuite } from '../suites/response-stream.mjs';
import { isSupervisorAutonomousPlayableLaneDefenseSuite } from '../suites/supervisor-autonomous-playable.mjs';
import {
isSupervisorSwarmSuite,
supervisorSwarmVerificationFixtureSource,
} from '../suites/supervisor-swarm.mjs';
import { isUserInputRuntimeSuite } from '../suites/user-input.mjs';
import { isWebSearchSuite } from '../suites/web-search.mjs';
import { createSentinelOwnedTempDirectory } from './app-data.mjs';
import { effectiveAgentLlmConfig } from './config.mjs';
@@ -41,19 +39,15 @@ import { runProcess } from './process.mjs';
import { isIsolatedRunnerSuite } from './reporting.mjs';
export function requiredAgentIdsForSuite() {
return isUserInputRuntimeSuite()
? [projectSupervisorAgentId]
: isSupervisorAutonomousPlayableLaneDefenseSuite()
? [projectSupervisorAgentId]
: isSupervisorSwarmSuite()
? [
projectSupervisorAgentId,
supervisorSwarmDesignAgentId,
supervisorSwarmQualityAgentId,
]
: isIsolatedRunnerSuite()
? [mainAgentId]
: [mainAgentId, 'quality-review'];
return isSupervisorSwarmSuite()
? [
projectSupervisorAgentId,
supervisorSwarmDesignAgentId,
supervisorSwarmQualityAgentId,
]
: isIsolatedRunnerSuite()
? [mainAgentId]
: [mainAgentId, 'quality-review'];
}
export function expectedProviderBindingForSuite(config) {
@@ -120,11 +114,9 @@ export async function checkPrerequisites(config) {
return {
llmConfigured,
providerBinding,
chromeAvailable:
!isIsolatedRunnerSuite() ||
isSupervisorAutonomousPlayableLaneDefenseSuite()
? Boolean(await findSupportedBrowser())
: false,
chromeAvailable: !isIsolatedRunnerSuite()
? Boolean(await findSupportedBrowser())
: false,
editorApiConfigured,
};
}
@@ -267,8 +259,7 @@ export async function seedDisposableProject({
? supervisorSwarmVerificationFixtureSource()
: isGoalRuntimeSuite() ||
isResponseStreamSuite() ||
isWebSearchSuite() ||
isSupervisorAutonomousPlayableLaneDefenseSuite()
isWebSearchSuite()
? goalRevisionOneVerificationFixtureSource()
: goalRevisionTwoVerificationFixtureSource(),
),
@@ -405,23 +396,6 @@ export function seededGameHtml() {
`;
}
export function productionDefaultGameIndexHtml() {
return `<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Genarrative Game Draft</title>
<style>
body { margin: 0; display: grid; min-height: 100vh; place-items: center; background: #101827; color: #d9e7ff; font: 16px system-ui, sans-serif; }
main { width: min(720px, calc(100vw - 32px)); }
</style>
</head>
<body><main>还没有生成游戏。回到聊天输入创意并确认生成后,这里会写入可试玩原型。</main></body>
</html>
`;
}
export function buildTaskPrompt(suite) {
const editorAssetOutcome =
suite === 'full'
@@ -20,9 +20,7 @@ import { isProjectSkillSuite } from '../suites/project-skill.mjs';
import { isResponseStreamSuite } from '../suites/response-stream.mjs';
import { isScopedAgentsSuite } from '../suites/scoped-agents.mjs';
import { isSteerRunnerKillSuite } from '../suites/steer-runner-kill.mjs';
import { isSupervisorAutonomousPlayableLaneDefenseSuite } from '../suites/supervisor-autonomous-playable.mjs';
import { isSupervisorSwarmSuite } from '../suites/supervisor-swarm.mjs';
import { isUserInputRuntimeSuite } from '../suites/user-input.mjs';
import { isWebSearchSuite } from '../suites/web-search.mjs';
import { isPathInside, readJson } from './io.mjs';
@@ -88,17 +86,12 @@ export function buildSummary() {
config: state.config,
blocked: state.blocked,
run: {
agentId:
isUserInputRuntimeSuite() ||
isSupervisorAutonomousPlayableLaneDefenseSuite() ||
isSupervisorSwarmSuite()
? projectSupervisorAgentId
: mainAgentId,
agentId: isSupervisorSwarmSuite()
? projectSupervisorAgentId
: mainAgentId,
runIdHash: hashValue(state.initialRunId),
sessionIdHash: hashValue(state.initialSessionId),
runnerKilled: isSupervisorAutonomousPlayableLaneDefenseSuite()
? false
: state.runnerKilled,
runnerKilled: state.runnerKilled,
resumed: state.resumed,
identityStable: state.identityStable,
},
@@ -110,11 +103,9 @@ export function buildSummary() {
projectPathReportLeakCount: state.projectPathReportLeakCount,
...(isWebSearchSuite() ||
isContextCompactionSuite() ||
isUserInputRuntimeSuite() ||
isScopedAgentsSuite() ||
isProjectSkillSuite() ||
isParallelReadSuite() ||
isSupervisorAutonomousPlayableLaneDefenseSuite() ||
isSupervisorSwarmSuite() ||
isSteerRunnerKillSuite()
? {
@@ -408,11 +399,9 @@ export function isIsolatedRunnerSuite() {
isResponseStreamSuite() ||
isWebSearchSuite() ||
isContextCompactionSuite() ||
isUserInputRuntimeSuite() ||
isScopedAgentsSuite() ||
isProjectSkillSuite() ||
isParallelReadSuite() ||
isSupervisorAutonomousPlayableLaneDefenseSuite() ||
isSupervisorSwarmSuite()
);
}
@@ -87,11 +87,7 @@ import {
import { isProjectSkillSuite } from '../suites/project-skill.mjs';
import { isScopedAgentsSuite } from '../suites/scoped-agents.mjs';
import { isSteerRunnerKillSuite } from '../suites/steer-runner-kill.mjs';
import {
confirmSupervisorSwarmPendingActionsInChat,
isSupervisorSwarmInteractiveChatSuite,
isSupervisorSwarmSuite,
} from '../suites/supervisor-swarm.mjs';
import { isSupervisorSwarmSuite } from '../suites/supervisor-swarm.mjs';
import { killRunnerPidOnce, verifyOwnedRunnerForKill } from './app-data.mjs';
import { isPlainObject } from './config.mjs';
import {
@@ -1038,13 +1034,6 @@ export async function confirmPendingActions(
allowedTools = null,
shouldConfirm = () => true,
) {
if (isSupervisorSwarmInteractiveChatSuite()) {
await confirmSupervisorSwarmPendingActionsInChat(
allowedTools,
shouldConfirm,
);
return;
}
for (const pending of await findPendingActions()) {
if (state.confirmedActionIds.has(pending.actionId)) continue;
if (!shouldConfirm(pending)) continue;
@@ -43,12 +43,6 @@ export const contextCompactionAppDataSentinelFileName =
export const contextCompactionAppDataSentinelSchema =
'genarrative-agent-runtime-real-e2e-context-compaction-appdata.v1';
export const userInputAppDataSentinelFileName =
'.agent-runtime-real-e2e-user-input-appdata.json';
export const userInputAppDataSentinelSchema =
'genarrative-agent-runtime-real-e2e-user-input-appdata.v1';
export const scopedAgentsAppDataSentinelFileName =
'.agent-runtime-real-e2e-scoped-agents-appdata.json';
@@ -109,30 +103,6 @@ export const supervisorSwarmToolPlanHandoffCheckpointReachedFileName =
export const supervisorSwarmToolPlanHandoffCheckpointReachedSchema =
'game-creator-tool-plan-handoff-runner-kill-checkpoint-reached.v1';
export const supervisorSwarmAutonomousChatAppDataSentinelFileName =
'.agent-runtime-real-e2e-supervisor-swarm-autonomous-chat-appdata.json';
export const supervisorSwarmAutonomousChatAppDataSentinelSchema =
'genarrative-agent-runtime-real-e2e-supervisor-swarm-autonomous-chat-appdata.v1';
export const supervisorAutonomousPlayableAppDataSentinelFileName =
'.agent-runtime-real-e2e-supervisor-autonomous-playable-appdata.json';
export const supervisorAutonomousPlayableAppDataSentinelSchema =
'genarrative-agent-runtime-real-e2e-supervisor-autonomous-playable-appdata.v1';
export const supervisorSwarmStaticIsolatedAutonomousChatAppDataSentinelFileName =
'.agent-runtime-real-e2e-supervisor-swarm-static-isolated-autonomous-chat-appdata.json';
export const supervisorSwarmStaticIsolatedAutonomousChatAppDataSentinelSchema =
'genarrative-agent-runtime-real-e2e-supervisor-swarm-static-isolated-autonomous-chat-appdata.v1';
export const supervisorSwarmCollaborationPolicyAppDataSentinelFileName =
'.agent-runtime-real-e2e-supervisor-swarm-collaboration-policy-appdata.json';
export const supervisorSwarmCollaborationPolicyAppDataSentinelSchema =
'genarrative-agent-runtime-real-e2e-supervisor-swarm-collaboration-policy-appdata.v1';
export const mainAgentId = 'code-prototype';
export const projectSupervisorAgentId = 'project-supervisor';
@@ -179,8 +149,6 @@ export const webSearchSuite = 'web-search';
export const contextCompactionSuite = 'context-compaction';
export const userInputRuntimeSuite = 'user-input-runtime';
export const scopedAgentsSuite = 'scoped-agents';
export const projectSkillSuite = 'project-skill';
@@ -213,18 +181,6 @@ export const supervisorSwarmTransientRetryBackoffMs = 30_000;
export const supervisorSwarmTransientRetryPreDueGuardMs = 1_500;
export const supervisorSwarmAutonomousChatSuite =
'supervisor-swarm-autonomous-chat';
export const supervisorAutonomousPlayableLaneDefenseSuite =
'supervisor-autonomous-playable-lane-defense';
export const supervisorSwarmStaticIsolatedAutonomousChatSuite =
'supervisor-swarm-static-isolated-autonomous-chat';
export const supervisorSwarmCollaborationPolicyMixedRecoverySuite =
'supervisor-swarm-collaboration-policy-mixed-recovery';
export const runtimeContextBundleSchemaVersion =
'game-creator-runtime-context-bundle.v5';
@@ -234,17 +190,6 @@ export const providerRequestLifecycleSchemaVersion =
export const providerActionBatchSchemaVersion =
'game-creator-provider-action-batch.v2';
export const runProfileBindingSchemaVersion =
'game-creator-run-profile-binding.v1';
export const autonomousCompletionContractSchemaVersion =
'game-creator-autonomous-completion-contract.v2';
export const autonomousPlaytestReceiptSchemaVersion =
'game-creator-autonomous-playtest-receipt.v2';
export const autonomousGameBuildRunProfile = 'autonomous-game-build';
export const supervisorCollaborationPolicySchemaVersion =
'game-creator-supervisor-collaboration-policy.v1';
@@ -287,12 +232,6 @@ export const contextCompactionTriggerTurns = new Set([4, 8]);
export const contextCompactionConstraintCanary = `GENARRATIVE_CONTEXT_CONSTRAINT_${randomUUID().replaceAll('-', '').slice(0, 20)}`;
export const userInputAnswerCanary = `GENARRATIVE_USER_CHOICE_${randomUUID()
.replaceAll('-', '')
.slice(0, 20)}`;
export const userInputAnswerText = `选择轻量像素风,优先保证移动端轮廓和动作可读性;确认标记 ${userInputAnswerCanary}`;
export const scopedAgentsRootCanary = `GENARRATIVE_SCOPE_ROOT_${randomUUID()
.replaceAll('-', '')
.slice(0, 20)}`;
@@ -461,143 +400,6 @@ export const supervisorSwarmConfirmedTools = [
'project.verify',
];
export const supervisorSwarmAutonomousTask =
'请把这个试玩项目推进到可以交给首批玩家体验的状态。以仓库现有的正式交付要求、临时检查要求和实际验证结果为准;按阶段生效的临时检查必须在各自生效后全部完成,不能把后续检查并入前置检查或漏掉。完成后简短说明交付内容、验证结论和仍需关注的问题。';
export const supervisorAutonomousPlayableLaneDefenseTask =
'做一个植物大战僵尸式的塔防游戏,要能选择植物、阻挡敌人、正常闯关,并且生成后可以直接试玩。';
export const supervisorAutonomousPlayableSourceFieldMaxChars = 8_000;
export const supervisorAutonomousPlayableSourceTotalMaxChars = 10_000;
export const supervisorAutonomousPlayableSafeStatuses = new Set([
'cancelling',
'budget-exhausted',
'cancelled',
'completed',
'failed',
'idle',
'needs-reconciliation',
'paused',
'pausing',
'pending',
'running',
'waiting-for-confirmation',
'waiting-for-user-input',
]);
export const supervisorAutonomousPlayableSafePhases = new Set([
'action',
'budget-exhausted',
'cancelled',
'cancelling',
'completion-contract-failed',
'completed',
'conversation-write-failed',
'executing',
'failed',
'finalizing',
'idle',
'needs-reconciliation',
'observation',
'parent-terminal',
'paused',
'pausing',
'planning',
'provider-action-batch',
'queued',
'response',
'running',
'waiting-for-agent',
'waiting-for-confirmation',
'waiting-for-delegate-receipts',
'waiting-for-isolated-join',
'waiting-for-process-session',
'waiting-for-provider-retry',
'waiting-for-user-input',
]);
export const supervisorAutonomousPlayableSafeDeliveryStatuses = new Set([
'claimed-by-parent',
'dispatched',
'ready',
'suppressed',
]);
export const supervisorAutonomousPlayableSafeTerminalStatuses = new Set([
'budget-exhausted',
'cancelled',
'completed',
'failed',
]);
export const supervisorAutonomousPlayableSafeTurnOutcomes = new Set([
'failed',
'incomplete',
'needs-reconciliation',
'settled',
]);
export const laneDefensePlaytestRequiredAssertions = [
'state-surface-valid',
'level-positive',
'start-control-clicked',
'start-sequence-advanced',
'start-phase-playing',
'defender-option-control-clicked',
'defender-selection-sequence-advanced',
'defender-selection-recorded',
'lane-cell-control-clicked',
'defender-placement-sequence-advanced',
'defender-count-increased',
'enemies-present-after-placement',
'speed-up-control-clicked',
'battle-sequence-advanced',
'battle-sequence-monotonic',
'enemy-position-changed',
'enemy-health-decreased',
'phase-won',
'next-level-control-clicked',
'next-level-sequence-advanced',
'level-increased',
'restart-control-clicked',
'restart-sequence-advanced',
'restart-phase-ready-or-playing',
];
export const supervisorSwarmAutonomousRoutingTerms = [
supervisorSwarmDesignAgentId,
supervisorSwarmQualityAgentId,
'agent.delegate',
'agent.run_status',
'同一个 planning 轮次',
'同轮',
'并行',
'返工',
'repair',
'一次',
'两个 Agent',
'两个专业',
'Runner',
'pidfd',
'actionId',
'delegationId',
];
export const supervisorSwarmCollaborationPolicyControlTerms = [
'.agent/collaboration-policy.json',
supervisorCollaborationPolicySchemaVersion,
'requiredInitialWave',
'minStaticDelegates',
'requiredStaticAgentIds',
'minIsolatedChildren',
'minIsolatedGroupsBeforeClaim',
'orchestratorOnlyAfterDelegation',
'policyFingerprint',
'contractFingerprint',
];
export const supervisorSwarmIsolatedReviews = [
{
path: 'e2e/isolated-a/evidence.txt',
@@ -625,17 +427,6 @@ export const supervisorSwarmIsolatedReviews = [
},
];
export const supervisorSwarmIsolatedReviewGroups = [
supervisorSwarmIsolatedReviews.slice(0, 2),
supervisorSwarmIsolatedReviews.slice(2),
];
export const supervisorSwarmInitialIsolatedReviews =
supervisorSwarmIsolatedReviewGroups[0];
export const supervisorSwarmFollowupIsolatedReviews =
supervisorSwarmIsolatedReviewGroups[1];
export const webSearchBaselineApiUrl =
'https://api.github.com/repos/nodejs/node/releases/latest';
@@ -703,8 +494,6 @@ export const pollIntervalMs = 750;
export const runTimeoutMs = 30 * 60 * 1000;
export const supervisorAutonomousPlayableRunTimeoutMs = 60 * 60 * 1000;
export const supervisorSwarmTerminalSidecarCleanupTimeoutMs = 10_000;
export const processRunnerKillStartTimeoutMs = 5 * 60 * 1000;
@@ -938,10 +727,6 @@ export class BlockedError extends Error {
export const isolatedRunnerState = {
appDataDir: null,
platformSessionFixturePath: null,
platformSessionFixtureSourcePath: null,
platformSessionFixtureSha256: null,
platformSessionFixturePreviousEnv: undefined,
ownerToken: null,
createdAt: 0,
current: null,
@@ -968,9 +753,6 @@ export const state = {
linuxPidfdPythonPath: null,
windowsProcessHandlePowerShellPath: null,
cleanupInProgress: false,
userInputCliSession: null,
supervisorSwarmCliSession: null,
supervisorAutonomousPlayableCliSession: null,
status: 'FAIL',
suite: null,
options: null,
@@ -1082,23 +864,6 @@ export const state = {
finalReplyFingerprint: null,
reportLeakCount: 0,
},
userInput: {
requestId: null,
responseId: null,
actionId: null,
questionMessageId: null,
answerMessageId: null,
questionCount: 0,
optionCount: 0,
providerStartedBeforeKill: 0,
providerStartedAfterRestart: 0,
conversationCountBeforeKill: 0,
conversationCountAfterRestart: 0,
oldRunnerBootId: null,
newRunnerBootId: null,
privateValues: [],
reportLeakCount: 0,
},
scopedAgents: {
effectiveModel: null,
effectiveApiKind: null,
@@ -1120,20 +885,6 @@ export const state = {
privateValues: [],
reportLeakCount: 0,
},
supervisorAutonomousPlayable: {
freshInitBaselineUsed: false,
initialGameIndexSha256: null,
expectedProviderBinding: null,
effectiveProviderBinding: null,
stdinWriteCount: 0,
stdinEnded: false,
stdinBytes: 0,
turnReport: null,
cliOutput: '',
privateValues: [],
reportLeakCount: 0,
ownedProcessCleanupSnapshot: null,
},
supervisorSwarm: {
effectiveModel: null,
effectiveApiKind: null,
@@ -1215,47 +966,7 @@ export const state = {
toolPlanHandoffLifecycleClosedExactlyOnce: false,
toolPlanHandoffAuditIdempotent: false,
toolPlanHandoffRecoveredPlanFingerprintMatched: false,
autonomousTaskRecipeFree: false,
autonomousRepositoryRecipeFree: false,
interactiveCliUsed: false,
chatSessionUnexpectedlyClosed: false,
chatSessionFailureKind: 'none',
chatSessionExitCode: 'none',
chatSessionCloseSignal: 'none',
chatSessionProcessErrorCode: 'none',
chatSessionStderrChars: 0,
chatSessionStderrSha256: 'none',
turnReport: null,
mixedSpawnActionId: null,
mixedSpawnRequestHash: null,
mixedFollowupSpawnActionId: null,
mixedFollowupSpawnRequestHash: null,
staticIsolatedProviderRequestIds: [],
staticIsolatedProviderOverlapObserved: false,
preKillMixedIdentity: null,
collaborationPolicyWritten: false,
collaborationPolicySnapshotInitialRecord: null,
collaborationPolicySnapshotInitialBytes: null,
collaborationPolicySnapshotInitialBytesSha256: null,
collaborationPolicySnapshotInitialIdentityHash: null,
collaborationPolicySnapshotBindingInitialRecord: null,
collaborationPolicySnapshotBindingInitialBytes: null,
collaborationPolicySnapshotBindingInitialBytesSha256: null,
chatSessionFailureDiagnostic: null,
collaborationPolicyDriftFixtureWritten: false,
collaborationPolicyDriftFixturePolicyFingerprint: null,
collaborationPolicyDriftedMinIsolatedGroupsBeforeClaim: null,
initialBatchRecoveryBoundaryObserved: false,
initialBatchRecoveryOldRunnerBootId: null,
initialBatchRecoveryNewRunnerBootId: null,
initialBatchRecoveryPreKillIdentity: null,
initialBatchRecoveryPostRecoveryIdentity: null,
initialBatchRecoveryPreKillSideEffects: null,
initialBatchRecoveryPostRecoverySideEffects: null,
initialBatchRecoveryIdentityStable: false,
initialBatchRecoveryConfirmationRestoredCount: 0,
initialBatchRecoveryPidfdClaimCount: 0,
initialBatchRecoveryPidfdSignalCount: 0,
},
confirmedActionIds: new Set(),
cleanupPerformed: false,
File diff suppressed because it is too large Load Diff

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