Merge remote-tracking branch 'origin/master'
# Conflicts: # apps/ai-game-creator-shell/src-tauri/src/project/resource_editor.rs
This commit is contained in:
@@ -7189,8 +7189,8 @@
|
||||
|
||||
- 路由决策:普通 AGC 登录后直接调用固定官方 origin 下的网站现役 `/api/editor/*`、`/api/assets/*` 与 `/api/runtime/external-generation/jobs/*`,不再要求用户配置 External Editor Base URL 或 `tnr_sk_...`。普通 release 的官方 origin 继续服从 2026-08-10 的 dev API 固定规则,本地 dev 使用配套代理。`/api/external/v1` 的 Bearer Developer API Key 契约保持独立且不接受网站 Access Token,只供第三方 Agent/CLI、独立 standalone game-chat release 与显式高级自定义模式;服务端不为普通用户自动签发、代管或下发 Developer Key。
|
||||
- UI 与 standalone 例外:普通 Launcher、工作台、已认证 debug game-chat 和运行时配置页不展示或维护 External Editor URL/Key。独立 standalone game-chat release 仍绕过 `AuthenticatedClient`,只能在用户显式配置其隔离 AppData 的高级 External v1 URL/Key 后使用云端画板能力;凭据缺失时明确不可用,不得回退到普通登录态、内置 Key 或隐式公共账号。
|
||||
- 会话 CAS:WebView 登录、refresh、退出和换号通过窄 Tauri/Runner 协议同步 `userId + 短期 Access Token + authGeneration` 到 GUI/Runner 进程内存。generation 单调递增,只有不旧于当前值的安装/清除可以生效;每个业务请求在发出前冻结 `ownerUserId + authGeneration`,在响应解析、账本推进、媒体安装和正式项目提交前都重新核对。旧 generation 的迟到安装、响应或清除不得覆盖新账号,也不得把旧账号产物安装到新账号项目。
|
||||
- 凭据边界:Access Token、refresh Cookie、Authorization 和 authGeneration 不进入 AppData、项目、manifest、生成账本、Agent prompt、conversation、Agent DB、trace、日志或普通错误;durable 账本只保存非敏感 ownerUserId。Runner IPC 只允许专用 session install/clear 消息携带 Token,普通生成/恢复命令参数继续不得携带 Access Token 或 Developer Key。
|
||||
- 会话 CAS:GUI 取得 owner OS 锁时产生随机 `owner epoch`,并持久化只含 `owner epoch + session revision` 的私有 claim。WebView 登录、refresh、退出和换号先单调推进 durable session revision,再以 `runner.attach_gui_owner` 同步 `userId + 短期 Access Token + authGeneration`;Runner 只接受 epoch/revision 与 durable claim 完全一致的 attach。同一 epoch 内 generation 仍单调递增,只有不旧于当前值的安装/清除可以生效;新 GUI epoch 则可替换旧进程留下的更高 generation。每个业务请求在发出前冻结 `ownerUserId + authGeneration`,在响应解析、账本推进、媒体安装和正式项目提交前都重新核对。旧 epoch/revision/generation 的迟到 attach、响应或清除不得覆盖新账号,也不得把旧账号产物安装到新账号项目。
|
||||
- 凭据边界:Access Token、refresh Cookie、Authorization 和 authGeneration 不进入 AppData、durable claim、项目、manifest、生成账本、Agent prompt、conversation、Agent DB、trace、日志或普通错误;durable claim 只保存非敏感 epoch/revision,durable 账本只保存非敏感 ownerUserId。Token 只允许随与 durable claim 完全匹配的 `runner.attach_gui_owner` 进入 Runner 内存;旧 `platform.session.install/clear` 不再是授权入口,普通生成/恢复命令参数继续不得携带 Access Token 或 Developer Key。claim 缺失、不可读或失配时 Runner 必须立即清空平台会话并阻断 Runtime 请求;同步失败必须隔离或停止旧 Runner,不得在 GUI 已切换后继续使用旧账号权威。
|
||||
- 401/refresh:普通模式首次 `401` 进入当前 authGeneration 的全局单飞 refresh;其它请求等待同一 Promise,但各自 deadline、取消和账号归属独立。refresh 成功只在原 generation/owner 仍当前时以更高 generation CAS 发布新 Token,然后每个调用最多使用原 endpoint、原始请求字节和原 `Idempotency-Key` 重试一次。refresh 失败、账号已切换或重试仍为 `401` 时停止网络并保留原 operation;`403` 表示当前账号权限不足,不触发 refresh。高级 External v1 模式只允许用户修正自己的 Developer Key,并继续原 operation。
|
||||
- Durable 身份与换号:普通账本身份固定为 `official-platform-v1 + 官方 origin + ownerUserId`,高级账本身份固定为 External service origin,二者均不绑定可轮换 Token/Key。退出或换号会提升 generation、中止并脱离旧请求;新账号对旧 owner 的 `prepared/accepted/running` 账本必须零 POST、零 GET、零下载、零安装、零归档,只有重新登录同一 owner 后才能恢复。`401/403/404`、网关错误或轮询超时不能在 owner 不匹配时证明旧 operation 的终态。
|
||||
- 幂等与站内响应:`prepared` 只可精确重放账本冻结的原 endpoint、原始正文和原幂等键;`accepted/running` 只查询原 operationId。Access Token/Key 轮换、refresh、重启、退出或换号都不得创建替代 operation、修改正文或重新扣费。站内生成适配 `200 + queueState.operationId`、inline 完成和 `job` 状态包装;External v1 继续固定 `202 + operationId`,两者共享相同 owner、计费、稳定引用与本地事务门禁。
|
||||
@@ -14363,6 +14363,31 @@
|
||||
- 素材生成确认、离开确认和旧服务身份确认沿用现有独立 modal,不在当前面板下方追加内容。modal 打开后焦点必须进入对话框,并同时隔离工具栏、画布视口、缩放/小地图、状态区和并存操作层,使背景从键盘焦点顺序与 accessibility tree 中退出。
|
||||
- `Tab` / `Shift+Tab` 必须在当前 modal 内双向循环;非异步 pending 状态允许 `Escape` 安全关闭。关闭后优先恢复到原触发器,自动弹出的 modal 则回退到可操作的工具栏入口,不能把焦点遗留在已卸载节点或被隔离背景中。
|
||||
|
||||
## 2026-08-23 AGC 本地资源与 External Editor 账号绑定分离
|
||||
|
||||
- 权威边界:本地项目 ID、manifest asset、正式本地文件和内容摘要属于设备上的本地项目;`canvasProjectId / resourceId / assetObjectId / objectKey` 属于具体 External Editor 服务 principal。manifest 中现有远端字段继续保留生成来源,不再承担“当前账号可编辑句柄”,本轮不修改共享 manifest schema。
|
||||
- 私有投影:统一在 `.agent/runtime/external-editor-bindings/` 持久化 active binding。项目 binding 按服务 origin、平台 userId 或 Developer Key 摘要以及本地 `projectId` 分区;资源 binding 再绑定远端项目、本地 asset ID、源 SHA-256、媒体类型和 canonical asset kind。sidecar 不保存 Token、Key、Cookie、Authorization、签名上传表单、媒体正文或绝对路径。
|
||||
- binding 完整性与并发:lookup key 与稳定 payload 内容指纹分开校验,远端 project/folder/resource/object ID、object key 和尺寸的任一改写都必须失败关闭。同一 `本地项目 + principal` 的首次 binding 建立串行化覆盖复读、远端创建与安装,并发生成不得各自创建画布或留下孤儿项目。
|
||||
- binding schema 迁移:当前 project/resource sidecar 显式为 v2 且 payload fingerprint 必填。旧 v1 文档仅能由独立 `deny_unknown_fields` wire 结构命中,在 key、principal、local project/source、remote 引用、object key、时间和尺寸等现役不变量通过后计算指纹并原子回写 v2;未知字段或身份 / source / 路径篡改均失败关闭且不回写。
|
||||
- 切号语义:账号 B 打开账号 A 曾生成资源的同一本地项目时,不访问、迁移或覆盖 A 的私有画布;B 从本地正式文件在自己的远端项目重新上传、confirm、登记并形成独立 binding。切回 A 时复用 A binding。项目标题只用于远端展示,不能作为同名项目的权威关联;项目改名不换 binding。
|
||||
- Runner 切号权威:Runner 协议 v7 规定 GUI owner 锁每次取得都创建新的随机 owner epoch,每次平台会话变更先推进 durable session revision claim。Runner 只能通过与该 claim 完全匹配的 `runner.attach_gui_owner` 替换会话,因此新 GUI 即使从较低 `authGeneration` 开始也能取代旧进程权威。claim 失配时立即清空 Runner 平台会话并阻断 Runtime,旧 `platform.session.install/clear` 只失败关闭而不再变更权威;GUI 与 Runner 同步失败时必须隔离或停止旧 Runner。
|
||||
- renderer / native 会话提交:renderer auth generation 只负责 UI 转换和迟到读取失效,native install / clear 使用独立只增 generation 并在同一串行队列执行。登录、refresh、当前用户查询与 native commit 共用请求前冻结的 API origin,refresh singleflight 按 origin 分区;入队前冻结 `user + token + origin`。候选账号只在 native 成功且仍属当前 auth generation 时提交;期望权威显式表示为 account 或 `null`。迟到 native 完成必须以更高 generation 对账回当前期望权威,对账失败清空 renderer committed 会话与 Token。
|
||||
- stale refresh 决策:queued commit 的 expected generation 过期时,early return 先恢复 renderer 当前 committed / desired Token。旧账号的 refresh 失败在当前 owner 已变更时只返回 `stale`,不发送会让 `AuthenticatedClient` 清空新账号的 failed 结果,也不执行新账号 native clear。
|
||||
- 在途边界:资源编辑、素材画布生成和 Agent 美术生成的 `prepared / accepted / running` 账本继续锁定发起 principal。账号切换后只能停止请求并保留原 operation 供切回或对账,不得把在途副作用迁移到新账号,也不得重建正文或重复 POST。只有尚未提交远端生成的新操作可以为当前账号建立新的项目和资源 binding。
|
||||
- 在途会话复验:提交返回 `operationId` 后先持久化 accepted 身份,再在每次 poll、download 和本地 commit 前校验冻结会话;同步本地 commit 必须持有冻结会话租约,使切号与安装线性化。Tauri 手工生成入口也必须使用稳定的 durable operation slot,不能退化成临时幂等键。切号后不得继续旧账号网络或把旧账号结果安装到本地项目。
|
||||
- 账本 owner 线性化:资源编辑与素材画布的 owner 绑定、服务身份指纹 / 挑战和确认写入,均在同一冻结 platform session 租约下完成,且账本已有 owner 必须精确匹配 `userId + api origin`。Developer Key 账本保持 `owner=null`;请求 / 确认挑战的直接命令调用也不具备跨 owner 写入权限。
|
||||
- 恢复投影:资源恢复列表持有当前 session 租约扫描,远端账本只对精确 owner `userId + api origin` 可见;未绑 owner 的 Developer / legacy 远端账本与 owner 不完整账本均失败关闭,纯本地编辑仍可见。renderer auth generation 变更同步作废恢复 read epoch,清空列表、挑战和动作状态,迟到读取不得恢复旧账号投影。
|
||||
- Direct 审计对账:图集只读恢复已完成 file + manifest 登记后,`asset.register` 追加审计一旦成功或结果未知,后续登记 / binding 失败都保留 file、manifest 和已落盘 audit,统一进入 `reconciliation-required`。禁止为了伪造原子性而回滚 manifest、删文件、重试未知审计或重新生成。
|
||||
- 账本迁移:资源 canonical 身份从历史 manifest `resourceId` 收口到本地 asset ID 时,旧账本只在 source asset、路径和 SHA-256 一致时可白名单恢复;新请求仍只信任本地 canonical 身份。
|
||||
- 集成边界:`resource_editor`、素材画布参考准备、`canvas.asset_generate` 的 art-spec 派生和直连只读恢复统一消费当前 principal binding;PR 176 rebase 后必须删除或整合其局部 canonical cache,不能保留第二套账号身份系统。External v1 的项目、素材目录和项目资源创建接口新增可选 `Idempotency-Key` 请求头并同步 OpenAPI;客户端用 binding key 派生稳定值,服务端按 `owner + 接口命名空间 + key` 生成稳定 ID,同键同正文返回原记录、同键异正文冲突。
|
||||
- 验收:至少覆盖 A 生成到本地后切 B 重登记、B 请求零 A 远端 ID、重启后复用 B、切回 A 复用 A、两个同名本地项目隔离、项目改名不漂移、源 SHA 或 kind 变化重登记、A 在途任务切 B 零网络,以及 sidecar 零凭据和身份篡改失败关闭。
|
||||
|
||||
## 2026-08-23 AI 游戏运行视窗按预览文档实际尺寸自适应
|
||||
|
||||
- 背景:项目开发工作台的中央运行视窗尺寸小于部分生成游戏的页面布局高度时,滚动条来自 loopback iframe 内部;宿主只隐藏 overflow 会直接裁掉标题、Canvas 或控制区,不能满足完整试玩。
|
||||
- 决策:客户端本地 preview server 为 UTF-8 HTML 注入固定同源尺寸桥;注入器按真实 HTML tokenizer 边界保守处理注释异常结束、DOCTYPE 引号、script escaped / double-escaped、raw-text、template、plaintext、foreign content 与重复 `src`,省略结束标签时只在已证明安全的文档位置注入。桥通过根节点 `ResizeObserver`、页面 load、窗口 resize 与字体就绪重新测量;页面可见时以 `500ms` 低频兜底探测至多 `512` 个元素边界,探测截断时不采用可能低估的部分样本,并排除随 viewport 同步变化的布局自反馈。它不订阅整页 DOM 突变,并只在尺寸元组真实变化时上报文档与浏览上下文宽高。宿主只接受当前 iframe source 与当前授权 loopback origin 的固定版本消息,按实际内容和可用容器计算最大为 `1` 的等比缩放并居中显示;首次缩放后仍接受内容宽高真实变化,重复内容尺寸或仅 viewport 回灌不更新状态,容器 resize 后重新以原生视口测量。运行视窗不再提供 iframe 横纵滚动条,内容适配不改游戏文件、manifest、PreviewRegistry 或运行业务状态,非 UTF-8 HTML 保持原样。
|
||||
- 验证:前端纯函数覆盖无需缩放、纵向超高缩放、首次缩放后的增高 / 缩短、重复内容尺寸去重、过期 viewport 与非法消息;Rust preview server 测试锁定尺寸去重、无全页 MutationObserver、低频有界探测、截断保护、固定 body 与 viewport 耦合布局不振荡、真实 HTML 上下文注入、注释异常结束、DOCTYPE 引号、script escaped / double-escaped、raw-text / template / plaintext / foreign content、省略结束标签、大小写结束标签、重复 `src` 和幂等注入;再以桌面最小窗口和更高窗口人工确认完整画面、动态内容变化后仍适配、无纵向滚动条且指针 / 键盘交互仍可用。
|
||||
|
||||
## 2026-08-23 Direct Codex 显式重生成与切片一等资源
|
||||
|
||||
- 决策:`taonier_prepare_game_art` 使用 `reuse-or-create | regenerate` 两态合同;旧调用缺省复用,只有用户显式重做或换风格才允许重生成。`regenerate` 只绕过本地完整包复用,不绕过未决 External Editor operation;旧账本 prompt 与本次 prompt 不一致时必须进入结果未知/对账,零新 POST。
|
||||
|
||||
@@ -4840,6 +4840,24 @@
|
||||
- 处理:先查 kernel journal 的 `Memory cgroup out of memory` 和目标容器 ID,再把本地/预发完整容器 SpacetimeDB 上限统一为 `2g`;保留 page pool 限制。不要只增加 publish 重试,也不要把 `/healthz` 或首页改成数据库就绪探针。
|
||||
- 验证:用新空卷完成模块 publish、五服务启动和 Web/API smoke,并确认容器未 OOM、SpacetimeDB 与 API/Nginx 最终 healthy。
|
||||
|
||||
## 同一本地项目切换账号后不能继续信任 manifest 远端 ID(2026-08-23)
|
||||
|
||||
- 现象:本地资源文件仍存在,但账号 A 生成后切到账号 B,快速编辑、GIF/视频派生、art-spec 下游生成或直连恢复提示画布不存在、无权限或资源不属于当前项目;原请求重试仍失败。
|
||||
- 原因:本地 manifest 只有一组 `source.canvasProjectId / resourceId / assetObjectId`,旧实现把它同时当作历史来源和当前账号可编辑引用。新的 operation 虽已绑定 B,却会把 A 的 project/resource/object 身份配合 B Token 发出;另一部分链路又把 A 的画布 ID 与 B 按标题选出的项目严格比较。
|
||||
- 处理:manifest 远端字段只保留历史 provenance。所有远端编辑和派生先解析 `.agent/runtime/external-editor-bindings/` 中当前 principal 的项目/资源 binding;不存在时从本地正式文件按 asset ID、SHA-256、媒体类型和 canonical kind 在当前账号重登记。禁止按标题、manifest ID 或其他账号 committed ledger 自动采纳远端对象。A 的在途 operation 继续留在 A,B 只能开始自己的新操作。
|
||||
- 并发与升级陷阱:首次 binding 不能用无锁的“先查后建”,否则两个 generation 会各自创建远端项目/目录;进程锁只能压住当前存活客户端,远端创建还必须携带由 binding key 派生的稳定 `Idempotency-Key`,关闭“响应已返回但 sidecar 未落盘”时重启重复创建的窗口。canonical ID 变更也不能只改新请求指纹;必须用本地 asset、路径和内容摘要白名单恢复旧 accepted/committed 账本,避免升级后已付费结果永久无法恢复。
|
||||
- binding wire 升级陷阱:在仍标 v1 的 struct 上直接新增必填指纹,会让所有已有本地项目的 binding 反序列化失败,重现“画布项目丢失”。必须升为新 schema,用独立严格 legacy wire 只迁移完整通过现役不变量校验的旧文档;不能用 default 缺失字段或在身份校验前回写。
|
||||
- 在途切号陷阱:只在首次 POST 前校验 session 不足以锁定 principal。获得 `operationId` 后必须先落盘,后续每次 poll、download 和本地 commit 都要复验原 session;同步 commit 的“先校验再写入”仍有切号竞态,必须用会话租约把校验与本地安装线性化。手工 Tauri 入口若使用临时幂等键,也会在 202 后切号时丢失恢复身份。切号后继续用 A Token 轮询或安装 A 结果同样是账号边界缺失。
|
||||
- renderer / native 两阶段陷阱:只用一个 generation 同时表示 UI 转换和 native CAS,或在 Rust 确认前先替换 committed Token,会让迟到 install / clear 把新账号覆盖回旧账号。正确做法是 auth generation 与 native 只增 generation 分离,所有 native mutation 串行,入队前冻结 token + origin + user,并持续以显式 account / `null` 期望权威对账迟到完成。对账失败不能恢复未确认候选会话,而要清空 committed 会话与 Token。
|
||||
- auth origin 竞态陷阱:只在登录前持久化服务器选择不等于冻结事务 origin;请求 A 在途时若 UI 改为 B,返回的 A Token 可能被安装到 B origin。登录、hydrate 与 refresh 必须在首个请求前冻结 origin,让 HTTP 链与 native commit 共用该快照;同时按 origin 隔离 refresh singleflight,不能让 A 的 Promise 被 B 复用。
|
||||
- stale refresh 陷阱:请求在入队前写入候选 Token 后,若 queued commit 直接因 generation 过期返回却不恢复 renderer authority,本地请求会继续携带错账号 Token。另外,A refresh 失败晚于 B 登录成功时,若仍向全局发布 failed,`AuthenticatedClient` 会把 B 误登出。所有 stale early return 先恢复当前 committed / desired Token;旧 owner 的迟到 refresh failure 只返回 `stale`,不 clear、不发布 failed。
|
||||
- 账本写入租约陷阱:只在进入命令时读一次当前账号,仍可能在 owner 绑定、服务身份指纹 / 挑战或确认落盘前切到 B,从而向 A 账本写入 B 身份。这些写入必须持同一冻结 platform session 租约并精确校验 ledger owner;公开的 request / confirm 命令也必须自身完成 owner 门禁,不能依赖调用方曾经走过恢复流程。
|
||||
- 恢复列表泄漏陷阱:项目相同不代表账本对当前账号可见。如果列表只按 project / phase 扫描,切 B 后会展示 A 的 operation,迟到回包还可能把已清空的列表重新写回。扫描必须持当前 session 租约并按完整 owner `userId + api origin` 过滤;未绑 / 不完整的远端账本隐藏,纯本地编辑保留。renderer 还要用 auth generation 使恢复 read epoch 失效并清空相关操作状态。
|
||||
- 追加审计回滚陷阱:Direct 恢复中 `asset.register` 是 append-only 审计,返回错误不能证明 append 未持久。若 file / manifest 已落盘且审计成功或结果未知,删文件或回滚 manifest 会创造“审计已存在、资源却消失”的第二种不一致,重试还可能复制审计或重新扣费。应保留 file + manifest + audit 现场并标记 `reconciliation-required`;后续 binding 失败也使用同一语义。
|
||||
- Runner 跨 GUI 陷阱:WebView 的 `authGeneration` 会随 GUI 进程重启从较低值重新开始,但 busy Runner 可能仍持有旧 GUI 的高 generation。只用 generation CAS 会把新账号安装误判为过期;只把 OS owner 锁当作授权,或保留独立 `platform.session.install/clear` 入口,又会让 Runner 在单次 IPC 丢失后继续使用旧账号。Runner 协议 v7 由 owner 锁创建随机 epoch,每次会话变更先推进 durable revision claim,并且只允许与 claim 完全匹配的 `runner.attach_gui_owner` 安装会话。Runner 要持续比对 claim,失配立即清空平台会话并拒绝 Runtime 请求;GUI 同步失败还要隔离或停止旧 Runner,不能只向前端报错。
|
||||
- 验证:用真实平台会话 fixture 覆盖 A→B→重启→A,逐个断言 B 的 URL、请求体和稳定引用中没有 A 的 project/resource/object ID;另测同名 localProjectId 隔离、改名不漂移、源摘要变化、非 refine 参考、视频 committed objectKey、art-spec 派生以及在途 operation 切号零网络。Runner 回归还要覆盖高 generation 旧 epoch 被低 generation 新 epoch 正确替换、迟到旧 epoch/revision attach 失败关闭、claim 改写或同步失败后旧 Runner 零 Runtime 请求。Developer Key fixture 不能替代平台账号隔离证据。
|
||||
- 关联:`apps/ai-game-creator-shell/src-tauri/src/project/external_editor_bindings.rs`、`project/resource_editor.rs`、`project/asset_canvas/generation.rs`、`agent/generation/canvas_generation.rs`、`agent/direct_runtime.rs`。
|
||||
|
||||
## Direct 美术工具不能把“包存在”当成“本次已生成”(2026-08-23)
|
||||
|
||||
- 现象:用户明确要求重做美术或切换游戏主题,工具仍立即返回 `assets/art-spec.png`、`assets/direct-game-background.png`、`assets/art-spritesheet.png`;新需求没有 Provider operation,游戏继续使用旧图。切片虽然已经落盘,也可能不出现在资源管理或工具结果中。
|
||||
|
||||
Reference in New Issue
Block a user