Merge remote-tracking branch 'origin/master' into feat/smoother-dialog-history
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m50s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 4m30s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 4m51s
Project CI / Backend tests (pull_request) Successful in 4m53s
Project CI / Frontend tests (pull_request) Successful in 2m33s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m27s
Project CI / Native shell tests (pull_request) Successful in 5m47s
Project CI / Repository checks (pull_request) Successful in 3m4s

This commit is contained in:
2026-10-02 19:20:53 +08:00
335 changed files with 8429 additions and 241440 deletions
@@ -14,7 +14,7 @@ AI Game Creator Shell 采用 IDEA 风格的当前进程错误报告:错误事
- 命令失败按具体变体建模(Rust 枚举 + ts-rs 导出的 `type` 判别联合;顶层只放调用方要分流的类别,可枚举细分收进类型化枚举 `reason` 字段,无字段变体生成 `{ type }`,带载荷变体生成 `{ type } & 载荷类型`),前端按变体分流,**任何地方都不对错误文案做判断**:认得的业务变体(用户输入 / 前置条件 / 预期 4xx)由调用方消化并给用户反馈,永不进池;系统变体、未识别变体和结构化之外的拒绝原样抛出,走上面的兜底入口。认证命令的封装形态:`invokeClientAuth` 只是薄包装,把结构化拒绝原样装进**已有**的 `ClientAuthErrorWrapper`(载体只有一个 `ClientAuthError` 类型的 `error` 字段,值就是 ts-rs 生成的判别联合;不读变体字段、不塞 `context`,构造时把整份载荷 `JSON.stringify` 进 `Error.message`,上报事件因此拿到机器事实),不新增手写错误类;判定只写在 catch 子句里,无字段变体用本 catch 的固定文案,带载荷变体先 `as` 取自己的具名载荷类型(`reason` 是枚举时再 `switch (payload.reason)`)、再用它自己的字段拼上本次操作的上下文前缀,`default` 用 `expectNever` 在编译期挡住漏接变体。
- 客户端 API 自动采集只覆盖网络错误、408 和 5xx(`clientApi` 作为 `fetch` 的调用方在抛出前判定);预期的 4xx 登录/鉴权失败不进入错误报告池。
- Rust 侧通过 `app_log!` 将普通文本日志同时输出到 stderr 和 AppData `diagnostics/application.log`,超出 256 KiB 滚动到 `application.previous.log`;WebView 的 console 输出通过 `append_application_log` 镜像到同一 raw log,并在客户端桥接处再次脱敏;`read_diagnostic_logs` 只读取应用级日志。
- 这里有两套互不相干的东西,不要互相代入:**错误报告事件池**是进程内 `error_report` 的结构化事件(本次变更不动它,仍然只在内存里、提交时才生成 `events.jsonl`);**统一 Agent Runtime 错误事件**是项目内 sidecar `.agent/runtime/errors/<eventId>.json`,既不进事件池也不进报告包。因为报告包里的日志附件只有 AppData 应用日志,所以 sidecar 的同一份已脱敏诊断再作为**日志行**(不是报告事件)投影成两行:`agent.runtime.error`(身份行:eventId / source / stage / code / retryable / clientTurnId / elapsedMs / detailRef,全部是程序生成或调用方常量)与 `agent.runtime.error.detail`(详情行:hint / summary / detail / metadata,自由文本只出现在这里)。两行都由 `agent/runtime_error.rs` 从同一份 diagnosis 生成,不新增字段来源;落到日志前 summary 按 320 字符、detail / metadata 按(1200 / 200 字符)预算脱敏截断(summary 由调用方给,`direct_tool_bridge` 会传工具错误原文,而 `app_log!` 同时把整行写 stderr,那里没有 `sanitize_diagnostic_message` 兜底)。`sanitize_diagnostic_message` 命中凭据标记时替换的是**整行**,自由文本因此只放详情行:详情行被吃掉也不影响身份行定位事件。
- 这里有两套互不相干的东西,不要互相代入:**错误报告事件池**是进程内 `error_report` 的结构化事件(本次变更不动它,仍然只在内存里、提交时才生成 `events.jsonl`);**统一 Agent Runtime 错误事件**是项目内 sidecar `.agent/runtime/errors/<eventId>.json`,既不进事件池也不进报告包。因为报告包里的日志附件只有 AppData 应用日志,所以 sidecar 的同一份已脱敏诊断再作为**日志行**(不是报告事件)投影成两行:`agent.runtime.error`(身份行:eventId / source / stage / code / clientTurnId / elapsedMs / detailRef,全部是程序生成或调用方常量)与 `agent.runtime.error.detail`(详情行:hint / summary / detail / metadata,自由文本只出现在这里)。两行都由 `agent/runtime_error.rs` 从同一份 diagnosis 生成,不新增字段来源;落到日志前 summary 按 320 字符、detail / metadata 按(1200 / 200 字符)预算脱敏截断(summary 由调用方给,`direct_tool_bridge` 会传工具错误原文,而 `app_log!` 同时把整行写 stderr,那里没有 `sanitize_diagnostic_message` 兜底)。`sanitize_diagnostic_message` 命中凭据标记时替换的是**整行**,自由文本因此只放详情行:详情行被吃掉也不影响身份行定位事件。
- 报告面板只由自动诊断通知中的“查看并报告”打开,不提供聊天命令、崩溃页按钮或其他手动入口;默认选中当前快照中的全部事件,用户可取消不想提交的事件。允许填写最多 2,000 字中文描述并取消日志附件;本版本不支持截图或任意文件附件。
- 报告面板读取当前错误快照失败时,必须明确显示“错误事件暂不可用,请关闭后重试”,不能把失败误显示为“当前没有待报告的错误”。
- 通知中的“查看并报告”打开面板时必须保留该次通知快照;最新快照读取瞬时失败时使用这份 fallback 继续展示和提交,不能因先清空通知而丢失用户刚看到的事件。
@@ -5,7 +5,7 @@
原始版本:`2026-07-14`;口径复核:`2026-08-25`
> 当前口径(2026-08-25):本文件只保留 Runtime V1.1 的持久化、恢复、安全和验证约束。AGC 当前产品入口、DirectProject、语义工具和 UI workflow 以 [`AI游戏创作智能体App实施计划-2026-06-24.md`](./【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md)、当前 `apps/ai-game-creator-shell` 代码及 `docs/README.md` 为准;本文件与其冲突时以后者为准。旧 ToolHost / DirectHome 兼容条款不得重新成为产品入口或第二套 Runtime 真相。
> 当前口径(2026-10-02):本文件描述的自建 Agent Runtime 执行面(runtime driver / protocol / tools / actions)、`--agent-*` CLI、目标 / 协作协议与真实 e2e harness 已整体退役,实现、测试、门禁与 CI job 均已删除,见 [`【ADR】退役AGC独立Agent Runtime与CLI执行面-2026-10-02.md`](../../adr/【ADR】退役AGC独立Agent%20Runtime与CLI执行面-2026-10-02.md)。外部 Runner 仅保留编辑器桥 RPC 与 GUI owner 参与锁 / watchdog。AGC 当前产品入口、DirectProject、语义工具和 UI workflow 以 [`AI游戏创作智能体App实施计划-2026-06-24.md`](./【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md)、当前 `apps/ai-game-creator-shell` 代码及 `docs/README.md` 为准;本文件与其冲突时以后者为准。旧 ToolHost / DirectHome 兼容条款不得重新成为产品入口或第二套 Runtime 真相。
## 目标
@@ -861,13 +861,13 @@ V1.20 对标 Codex CLI 的可选 Web Search,但只声明当前 `platform-llm`
## V1.21 单 Agent token-aware 持久上下文压缩
V1.21 对标 Codex CLI 的 `model_context_window`、`model_auto_compact_token_limit`、`tool_output_token_limit` 和显式手动压缩。它替换“固定保留最近 12 条就算压缩”的能力口径,但不删除原始 conversation、task、event 或工具事实,也不把模型摘要提升为 Goal、计划、权限、验证或副作用事实源。
V1.21 对标 Codex CLI 的 `model_context_window`、`model_auto_compact_token_limit` 和显式手动压缩。它替换“固定保留最近 12 条就算压缩”的能力口径,但不删除原始 conversation、task、event 或工具事实,也不把模型摘要提升为 Goal、计划、权限、验证或副作用事实源。
### 配置与预算
- `llm` 新增 `contextWindowTokens / autoCompactTokenLimit / toolOutputTokenLimit`,发布默认分别为 `128000 / 64000 / 12000`;`agentLlm.<agentId>` 复用现有 patch 继承,显式 Agent 值覆盖全局。三项都必须大于 0,自动阈值必须小于 context window,并为当前请求的生成 token 预算与固定安全余量留下空间。既有配置和持久协议键 `maxOutputTokens` 保持冻结以兼容恢复;它表示包含可见输出与隐藏 reasoning token 的生成侧预算,不表示输入加输出总量,也不保证可见正文长度。
- `llm` 新增 `contextWindowTokens / autoCompactTokenLimit`,发布默认分别为 `128000 / 64000`;`agentLlm.<agentId>` 复用现有 patch 继承,显式 Agent 值覆盖全局。两项都必须大于 0,自动阈值必须小于 context window,并为当前请求的生成 token 预算与固定安全余量留下空间。既有配置和持久协议键 `maxOutputTokens` 保持冻结以兼容恢复;它表示包含可见输出与隐藏 reasoning token 的生成侧预算,不表示输入加输出总量,也不保证可见正文长度。
- Runtime 在发送 tool-plan、context-compaction 或 final-reply 前,按消息、multimodal 文本和 function schema 的规范序列化字符数做保守 token 估算;Provider 返回 usage 时再记录真实 `prompt/completion/total`。估算只用于提前门禁,不能伪装成 Provider 计费事实。
- 单条 observation 进入模型上下文前按 `toolOutputTokenLimit` 收紧;完整命令输出仍留在 owning Agent 的私有 sidecar,通过既有分页工具读取。公共状态只显示估算 token、最近真实 usage、阈值、压缩次数和时间,不显示被压缩正文。
- 完整命令输出仍留在 owning Agent 的私有 sidecar,通过既有分页工具读取。公共状态只显示估算 token、最近真实 usage、阈值、压缩次数和时间,不显示被压缩正文。
### 可压缩内容与不可压缩事实
@@ -921,7 +921,7 @@ V1.22 对标 Codex CLI 的 MCP tool 能力,在现有单 Agent Runtime 内增
- 共享命令契约新增 `mcp.call`,项目/Agent policy 仍可统一 deny 或 confirm。server/tool 配置只会进一步收紧:`deny` 直接返回 blocked observation,`confirm` 复用现有确认卡,`auto/writes` 也必须先可靠写入 durable pending action。隔离 child 默认禁止 MCP;后续若开放必须有模板级显式 allowlist,不继承父 Agent 的宽权限。
- MCP 调用复用当前 action fingerprint、steer cursor、Goal、project/repository revision、verification gate、Provider request identity 与 `approved -> executing -> observed-*` 账本。调用前再次读取 AppData、刷新目标 tool 并核对两个 fingerprint;Runner 在 `executing` 后退出、超时后无法确定服务端是否完成、SDK transport 断开或审计落盘失败均进入 reconciliation。只有服务端明确返回 result/error,才形成可继续 planning 的确定终态。
- 完整 `CallToolResult` 原子写入 `.agent/runtime/mcp-results/<agentHash>/<runHash>/<actionHash>.json` 私有 sidecar,绑定项目/Agent/Task/Session/Run/action/server/tool/catalog/tool/result fingerprint、执行时 `toolOutputTokenLimit`、字符/内容块计数、isError 和时间。恢复时必须从完整 result 按原预算重算计数、摘要、二进制元数据和 observation 并全量比对;任一派生字段不一致都进入 reconciliation,不能接受局部损坏摘要。模型 observation 只读取受 `toolOutputTokenLimit` 约束的 text/structured content;image/audio/embedded resource 只给类型、MIME、大小和 SHA-256 元数据,本切片不把任意 MCP 二进制转发给 Provider。
- 完整 `CallToolResult` 原子写入 `.agent/runtime/mcp-results/<agentHash>/<runHash>/<actionHash>.json` 私有 sidecar,绑定项目/Agent/Task/Session/Run/action/server/tool/catalog/tool/result fingerprint、字符/内容块计数、isError 和时间。恢复时必须从完整 result 按原预算重算计数、摘要、二进制元数据和 observation 并全量比对;任一派生字段不一致都进入 reconciliation,不能接受局部损坏摘要。模型 observation 只读取 text/structured content;image/audio/embedded resource 只给类型、MIME、大小和 SHA-256 元数据,本切片不把任意 MCP 二进制转发给 Provider。
- 公共 Runtime state/event/task/Agent DB/receipt/activity/output/report 只保存 server/tool、审批、状态、参数字符数与 SHA-256、结果内容块计数/字符数与 SHA-256、sidecar 相对路径和安全错误分类,不保存 arguments、返回正文、Bearer/header/env、server instructions、绝对路径或 SDK 原始错误。`agent.action_history` 同样只返回该安全摘要。
### 开发入口与验收
@@ -1,5 +1,9 @@
# AI 游戏创作智能体 App 实施计划
## 2026-10-02 退役自建 Agent Runtime 与 CLI 执行面
自建 Agent Runtime(`agent/runtime_driver`、`runtime_protocol`、`runtime_tools`、`runtime_actions`、`runtime_state`、`runtime_adapter`、`agent/prompt.rs`、`agent_native_tools.rs`、`collaboration.rs`、`delegation.rs`、`goal.rs`、`context_compaction.rs`、`isolated_agent.rs`、`tool_plan_handoff/`、`user_input.rs`)与它的 Tauri 命令、`--agent-*` CLI、真实 e2e harness、CI job 一并退役,历史由 Git 保存,见 `docs/adr/【ADR】退役AGC独立Agent Runtime与CLI执行面-2026-10-02.md`。本文正文里所有关于 `--agent-run` / `--agent-task` / `--agent-runner-status` / `agent-run:smoke` / `agent-runtime-real-e2e.mjs`、后台单 Agent 队列、`read_game_creator_agent_runtime(s)`、Supervisor 自主协作与 Goal 生命周期的描述均已失效,不再作为实现依据。外部 Runner(`--agent-runner`)仅保留编辑器桥 RPC 与 `runner.attach_gui_owner` + GUI owner 参与锁 / watchdog。AGC 现役链路以 DirectProject(`agent/direct_*` + codex app-server)、资源工作台与本文其后的 DirectProject 相关条目为准。
## 2026-10-02 固定试玩控件的指针命中边界
固定试玩只按目标控件自身的计算样式判断 `pointer-events:none`,不累计祖先的 `none`。覆盖层为 `none`、按钮显式恢复 `auto` 是合法布局;按钮未覆盖时仍会继承 `none` 并被拒绝。保留祖先可见性、中心点 `elementFromPoint` 命中、原生 disabled、aria-disabled 和 inert 检查;目标自身为 `none` 时,即使内部子元素恢复 `auto`,也不放行目标控件。
@@ -1689,9 +1693,9 @@ DirectProject 在收到完整游戏策划或游戏制作请求后,必须把视
## 2026-09-15 AGC 统一错误事件、诊断落库与验收反馈
DirectProject、Agent Runtime、Provider、app-server、内置 MCP、命令执行、构建和浏览器试玩的失败必须先转换为统一的 `AgentRuntimeErrorEvent`,再分别投影到用户消息、运行面板和项目诊断文件;业务模块不得自行拼接只有一句“执行失败”的终态文案。统一事件至少包含 `schemaVersion / eventId / clientTurnId / source / stage / code / retryable / occurredAt / elapsedMs / publicText / recoveryHint / detailRef`,其中 `publicText` 是脱敏后的可行动摘要,`detailRef` 指向项目内有界诊断记录;Token、Cookie、URL/query、私钥、宿主绝对路径、原始请求正文和未脱敏 stderr 不得进入对话或用户可见文本。
DirectProject、Agent Runtime、Provider、app-server、内置 MCP、命令执行、构建和浏览器试玩的失败必须先转换为统一的 `AgentRuntimeErrorEvent`,再分别投影到用户消息、运行面板和项目诊断文件;业务模块不得自行拼接只有一句“执行失败”的终态文案。统一事件至少包含 `schemaVersion / eventId / clientTurnId / source / stage / code / occurredAt / elapsedMs / message / error / detailRef`,其中 `message` 是产生失败的 typed 错误在失败现场写好、脱敏后的人类可读文案,`error` 是同一个 typed 错误 enum 的原样序列化(一个 case 一个变体,供开发者按变体与字段定位;没有 typed 错误的调用方写 `null`),`detailRef` 指向项目内有界诊断记录;Token、Cookie、URL/query、私钥、宿主绝对路径、原始请求正文和未脱敏 stderr 不得进入对话或用户可见文本。
项目内统一落库目录为 `.agent/runtime/errors/`,事件记录采用幂等 JSONL 或 JSON sidecar;写入失败不能覆盖原始业务错误,但必须在事件中标记 `persistenceFailed`。DirectProject 对话历史必须持久化本轮用户消息、终态错误的安全 assistant 投影和诊断引用,使下一轮能够读取上一轮失败证据。前端只展示 `publicText`,点击详情后按 `detailRef` 读取有界、脱敏的诊断,不直接展示私有 `detail`。
项目内统一落库目录为 `.agent/runtime/errors/`,事件记录采用幂等 JSONL 或 JSON sidecar;写入失败不能覆盖原始业务错误,但必须在事件中标记 `persistenceFailed`。DirectProject 对话历史必须持久化本轮用户消息、终态错误的安全 assistant 投影和诊断引用,使下一轮能够读取上一轮失败证据。前端只展示宿主给的安全文案;诊断正文只留在 `detailRef` 指向的有界、脱敏记录里,不进入用户可见文本。
前端取回 `detailRef` 的口径是失败文案末尾的固定后缀「;详情:<detailRef>」:Rust 侧 `direct_codex_failure_text_keeps_the_detail_ref_marker_for_the_renderer` 与前端 `tests/agentRuntimeErrorDetail.test.ts` 各自钉住同一份文案形状与它的解析,任一侧改文案或改解析都会变红。失败提示只展示映射后的安全 `publicText`(v1 历史形状与 v2 现行形状都映射成「阶段 + 摘要 + 建议 + 是否可直接重试」),诊断正文不在提示里预读、也不写入历史投影,而是由聊天状态栏下方的「查看详情」入口按需调用只读命令读取并二次脱敏;这条交互由 `tests/appSurface/chat-composer.suite.ts` 的「失败提示保留可执行原因,诊断正文只在「查看详情」时读取」与 `tests/agentRuntimeModel.test.ts` 的 v2 映射用例钉住,渲染层仍不参与错误分类。
@@ -1842,9 +1846,9 @@ Direct 回合的所有权属于进程内项目身份锁,不属于当前页面
## 2026-09-21 统一错误事件同时落到 AppData 应用日志
`AgentRuntimeErrorEvent` 把失败投影到用户消息、运行面板和项目内 `.agent/runtime/errors/<eventId>.json` 时,同一份已脱敏诊断还要投影成 AppData `diagnostics/application.log` 的两行:`agent.runtime.error`(身份行:`eventId / source / stage / code / retryable / clientTurnId / elapsedMs / detailRef`)与 `agent.runtime.error.detail`(详情行:`hint / summary / detail / metadata`)。原因是项目内 sidecar 只在项目目录可见,而“报告问题”只上传应用级日志:没有这两行时,用户提交的失败消息里只剩一个 `详情:.agent/runtime/errors/...json` 路径,团队拿不到诊断正文。
`AgentRuntimeErrorEvent` 把失败投影到用户消息、运行面板和项目内 `.agent/runtime/errors/<eventId>.json` 时,同一份已脱敏诊断还要投影成 AppData `diagnostics/application.log` 的两行:`agent.runtime.error`(身份行:`eventId / source / stage / code / clientTurnId / elapsedMs / detailRef`)与 `agent.runtime.error.detail`(详情行:`message / error / metadata`)。原因是项目内 sidecar 只在项目目录可见,而“报告问题”只上传应用级日志:没有这两行时,用户提交的失败消息里只剩一个 `详情:.agent/runtime/errors/...json` 路径,团队拿不到诊断正文。
口径:两行都由 `agent/runtime_error.rs` 从同一份 diagnosis 生成,字段不退化成第二份来源;`summary` 按 320 字符、`detail` 与 `metadata` 按(1200 / 200 字符)预算先脱敏再截断,落盘前还会被 `sanitize_diagnostic_message` 二次脱敏并按行截断,因此自由文本字段在行内先压平换行。拆两行是因为整行一旦出现凭据标记会被整体替换成脱敏占位:所以**自由文本(summary / hint / detail)只放详情行**,身份行只留程序生成与调用方常量字段,详情行被整体脱敏时事件仍能按 eventId / detailRef 定位。写日志先于写 sidecar:sidecar 失败不能连日志一起丢。
口径:两行都由 `agent/runtime_error.rs` 从同一份 diagnosis 生成,字段不退化成第二份来源;sidecar 里的 `message` 按 8 KiB 上限、应用日志的 `message` / `error` / `metadata` 按(1200 / 400 / 200 字符)预算先脱敏再截断,落盘前还会被 `sanitize_diagnostic_message` 二次脱敏并按行截断,因此自由文本字段在行内先压平换行。拆两行是因为整行一旦出现凭据标记会被整体替换成脱敏占位:所以**自由文本(message)只放详情行**,身份行只留程序生成与调用方常量字段,详情行被整体脱敏时事件仍能按 eventId / detailRef 定位。写日志先于写 sidecar:sidecar 失败不能连日志一起丢。
## 2026-09-23 AGC UI 设计文档 Agent 工具化重写