让 Direct 首轮带上本轮附件的项目路径映射 (#223)
抽出 Home/Project 共用的 DirectCodexTurnAttachment 与渲染函数 Direct command 在进 Codex 前拼接有界 sidecar,jsonl 与气泡仍写用户原文 首页建项 latch 把导入附件传给 Direct 首轮,后续手打消息不带 attachments 做方案首轮仍走 Supervisor,不注入 sidecar 补齐 Rust 渲染测试与 home.suite 附件断言 记录路径映射合同与决策 Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/223 Co-authored-by: 孔令弘 <ink29535@proton.me> Co-committed-by: 孔令弘 <ink29535@proton.me>
This commit was merged in pull request #223.
This commit is contained in:
@@ -20,6 +20,8 @@
|
||||
## AI 游戏创作与 Agent Runtime
|
||||
|
||||
- [AI 游戏创作智能体 App 实施计划](./technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md):当前 DirectProject、受控语义工具、UI workflow、资源和运行时合同。
|
||||
- [DirectProject 本轮附件路径映射](./technical/【技术方案】DirectProject本轮附件路径映射-2026-08-31.md):Direct 首轮只映射附件原名与项目相对路径,不灌正文、不区别 GDD。
|
||||
- [Direct 回合行为审计账本](./technical/【技术方案】Direct回合行为审计账本-2026-08-31.md):Direct GUI 回合把 native 读 / MCP / 写文件落成项目内有界时间线,用于判断有没有打开本轮附件。
|
||||
- [项目开发工作台 PRD](./prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md):当前工作台页面和验收边界。
|
||||
- [立项策划 Agent(Fast GDD)](<./technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md>):当前策划入口、审批和恢复合同。
|
||||
- [GameAgent 资源自由画板与快速编辑](./technical/【技术方案】GameAgent资源自由画板与快速编辑-2026-08-20.md)
|
||||
|
||||
@@ -25,6 +25,22 @@
|
||||
|
||||
---
|
||||
|
||||
## 2026-08-31 Direct 回合把 Codex item 落成有界行为账本
|
||||
|
||||
- 背景:sidecar 已让模型看见本轮附件路径,但 native 读 / MCP / 写文件只存在于隔离 `CODEX_HOME` 的瞬时 stdout,回合结束即删。无法判断「没读附件」还是「读了仍走默认收集类」。
|
||||
- 决策:GUI DirectProject 每个 `clientTurnId` 追加 `.agent/runtime/direct-codex/turns/<id>.jsonl`,并在 `agent.db` 写一条 `direct.codex.turn` 摘要。记 sidecar 提供的路径与文件 hash、`item/completed` 的 Read/List/Search/MCP/写文件(不含 stdout、patch、MCP result),以及 `offeredRead` / `firstDesign`。审计 fail-open,不阻断做游戏。Home、CLI、Supervisor 收据模型不接。不灌附件正文,不强制读取,不为 GDD 开特例。
|
||||
- 影响范围:`direct_codex_audit.rs`、Direct GUI command 边界、Codex collect 循环;前端 / jsonl 气泡 / sidecar 文案不变。
|
||||
- 验证方式:Rust fixture 覆盖 turn_start hash、绝对路径相对化、stdout/diff 不落盘、art brief 保留、list/search 不算已读、firstDesign 顺序、256 条截断、写盘失败不 panic;sidecar 渲染与 Direct 活动词测试保持通过。
|
||||
- 关联文档:`docs/technical/【技术方案】Direct回合行为审计账本-2026-08-31.md`、issue #212。
|
||||
|
||||
## 2026-08-31 Direct 本轮附件只映射路径,不灌正文、不区别 GDD
|
||||
|
||||
- 背景:issue #212。首页附件已经复制到 `assets/uploads/` 并登记,但 Direct 首轮只把用户原文发给 Codex,原文件名不是磁盘路径,模型会另起一套玩法。
|
||||
- 决策:Home 与 Project 共用 `DirectCodexTurnAttachment`。有项目路径或导入状态时,只在发给 Codex 的 user prompt 末尾附有界 sidecar(原名 → 项目相对路径、类型、大小、状态);无路径且无状态时保持首页元数据文案。不灌正文、不强制读取、不按 GDD 开特例。做成游戏固定 prompt 不改,同一条 Direct 首轮附件链自动吃到 sidecar。jsonl 与工作台气泡仍只写用户原文。
|
||||
- 影响范围:`direct_codex_attachments.rs`、Direct command 边界、首页建项 latch、工作台首轮 invoke;Supervisor / 做方案首轮忽略附件 sidecar。
|
||||
- 验证方式:Rust 渲染测试(Home 逐字兼容、Project 映射、非法路径);home.suite 附件 Direct invoke 含 `localPath`;无附件不出现 `attachments` 键;做方案首轮仍走 Supervisor 且无 sidecar;后续手打消息不带 attachments。
|
||||
- 关联文档:`docs/technical/【技术方案】DirectProject本轮附件路径映射-2026-08-31.md`、issue #212。
|
||||
|
||||
## 2026-08-26 运行中自主扩图提案留在编排层
|
||||
|
||||
- 背景:`agent-runtime-orchestration` 已能构造和调度动态 DAG,但 LLM 在执行中发现缺少步骤时没有通用的安全扩图合同。
|
||||
@@ -39,6 +55,7 @@
|
||||
- 产品边界:16 个游戏任务、六组角色、产物/验收条件、Evaluator Markdown 和中文语义路由继续留在 `platform-agent`;AGC 组合根使用公共层校验任务图与 `AgentCatalog`。Runtime store、Runner、Provider、权限、ToolHost、委派 journal、isolated write scope 和 `.agent/runtime/**` 不迁移、不双写。
|
||||
- 验证方式:非游戏 conformance 覆盖并行分支、汇合、repair closure、AgentCatalog 和非法图失败关闭;`platform-agent` 锁定种子 DAG 与现役波次/返工顺序,并验证环拒绝和 catalog 注入。根检查脚本必须执行新 crate 测试。
|
||||
- 关联文档:`docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md` V1.54。
|
||||
|
||||
## 2026-08-27 `plan.submit_gdd` 拒绝无审批决定的 `user_revision`
|
||||
|
||||
- 背景:结构校验允许 `round=0 + user_revision + confirmed`,提交闸原先只做结构、身份和 Session CAS。Provider 可在首次 collecting、澄清续跑或提交前质量返工里把未确认项标成用户审批修改,审批卡显示「已确认」。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 文档地图与阅读索引
|
||||
|
||||
更新时间:`2026-08-25`
|
||||
更新时间:`2026-08-31`
|
||||
|
||||
## 阅读顺序
|
||||
|
||||
@@ -24,9 +24,11 @@ AI 游戏创作 / DirectProject / UI workflow:
|
||||
1. `docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`
|
||||
2. `docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`
|
||||
3. `docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md`
|
||||
4. `docs/technical/【技术方案】GameAgent资源自由画板与快速编辑-2026-08-20.md`
|
||||
5. `docs/【技术方案】UI工作流资源桥接与Runtime执行-2026-08-24.md`
|
||||
6. UI 编辑器、宿主壳和当前测试专题文档
|
||||
4. `docs/technical/【技术方案】DirectProject本轮附件路径映射-2026-08-31.md`
|
||||
5. `docs/technical/【技术方案】Direct回合行为审计账本-2026-08-31.md`
|
||||
6. `docs/technical/【技术方案】GameAgent资源自由画板与快速编辑-2026-08-20.md`
|
||||
7. `docs/【技术方案】UI工作流资源桥接与Runtime执行-2026-08-24.md`
|
||||
8. UI 编辑器、宿主壳和当前测试专题文档
|
||||
|
||||
图片画布 / 媒体生成:
|
||||
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
# DirectProject 本轮附件路径映射
|
||||
|
||||
- 日期:2026-08-31
|
||||
- 状态:现行合同(已按本文落地)
|
||||
- 问题:Gitea issue #212(DirectProject 未消费用户上传权威文档)
|
||||
- 关联入口:PR #210「批准 GDD 回填做游戏入口」(`feat/create_entrance`,未合入时仍按该 PR 的调用链理解)
|
||||
- 原则:落地后代码简洁可维护,不为了 diff 最小而打补丁;附件一律同等对待,不给 GDD 开协议特例
|
||||
|
||||
## 0. 一句话
|
||||
|
||||
首页带进项目的附件已经复制并登记,但 Direct 首轮只拿到用户原文。本方案让 Direct 回合在发给 Codex 的 user prompt 末尾附上**有界路径映射**(原文件名 → 项目相对路径),不灌正文、不强制读取、不改做方案注入。
|
||||
|
||||
## 1. 目标与非目标
|
||||
|
||||
### 目标
|
||||
|
||||
1. Direct 首轮知道本轮用户附件的原文件名、项目相对路径、媒体类型、大小和导入状态。
|
||||
2. 用户原文不被改写;路径映射是独立 sidecar。
|
||||
3. 图片、Markdown、其它文件走同一条协议。
|
||||
4. 做成游戏(PR #210:读 `game/fast_gdd.md` → 当成 `fast_gdd.md` 附件 → `createHomeDraftAutomatically`)自动吃到效果,因为那条链就是「带附件的 Direct 首轮」。
|
||||
|
||||
### 非目标
|
||||
|
||||
- 不改 PR #210 的按钮、固定 prompt、`startGameFromApprovedGdd`、读 `game/fast_gdd.md` 的方式。
|
||||
- 不改 `approvedGddRef`、审批 receipt、策划项目里的 `game/fast_gdd.md` 投影。
|
||||
- 不改上传命名 `assets/uploads/upload-<ts>-<name>`。
|
||||
- 不把附件全文拼进 prompt,不按扩展名决定是否读取。
|
||||
- 不把「没读到就阻断」做成门禁。
|
||||
- 不扫 manifest 里历史 `kind=uploaded`。
|
||||
- 不做 native 读取审计;该项由 [`【技术方案】Direct回合行为审计账本-2026-08-31.md`](./【技术方案】Direct回合行为审计账本-2026-08-31.md) 承接。
|
||||
- 不改 DirectHome 在「无项目路径」时的现有文案和列表格式。
|
||||
- 不改 `enterCreatedHomeProject` 的空正文兜底句(与做方案共用)。
|
||||
|
||||
## 2. 现行断点
|
||||
|
||||
```text
|
||||
Home 附件 / 做成游戏 File(fast_gdd.md)
|
||||
→ upload_local_asset
|
||||
→ LauncherProjectContext.attachments // 已有,只给资源画布
|
||||
→ ProjectSupervisor // 无 attachments 字段
|
||||
→ chat_with_game_creator_direct_codex
|
||||
{ projectPath, prompt, clientTurnId, creationType? }
|
||||
```
|
||||
|
||||
做成游戏的固定 prompt 仍写「附件中的 `fast_gdd.md`」,磁盘文件却是 `assets/uploads/upload-<ts>-fast_gdd.md`。映射没有进 Direct。
|
||||
|
||||
DirectHome 已有 `{ name, mediaType, size }` 元数据注入,但标明「尚未打开项目,内容尚不可读取」。项目已落盘后这条元数据被丢掉。
|
||||
|
||||
## 3. 目标合同
|
||||
|
||||
### 3.1 唯一 DTO
|
||||
|
||||
Home 与 Project 共用一个附件结构,缺省字段表示 Home 现状:
|
||||
|
||||
```ts
|
||||
{
|
||||
name: string; // 原文件名
|
||||
mediaType: string;
|
||||
size?: number; // 缺省按 0
|
||||
localPath?: string; // 仅已落入项目时出现
|
||||
status?: 'imported' | 'failed';
|
||||
}
|
||||
```
|
||||
|
||||
- 不把 `error` 字符串送给模型。
|
||||
- 前端 `LauncherImportedAttachment` 继续给画布;invoke 前映射成上述瘦 DTO。
|
||||
- `importHomeAttachments` 把 `File.size` 写入可选 `size`,不改 `upload_local_asset` 返回值。
|
||||
|
||||
Rust:
|
||||
|
||||
```rust
|
||||
struct DirectCodexTurnAttachment {
|
||||
name: String,
|
||||
media_type: String,
|
||||
#[serde(default)]
|
||||
size: u64,
|
||||
#[serde(default)]
|
||||
local_path: Option<String>,
|
||||
#[serde(default)]
|
||||
status: Option<String>, // 只接受 imported | failed,其它忽略
|
||||
}
|
||||
```
|
||||
|
||||
`DirectCodexHomeAttachment` 删除,Home command 改用同一类型。现有 Home JSON(无 `localPath` / `status`)继续能反序列化。
|
||||
|
||||
### 3.2 渲染
|
||||
|
||||
一个函数 `render_direct_codex_user_prompt(prompt, attachments) -> Result<String, String>`:
|
||||
|
||||
| 输入 | 输出 |
|
||||
|---|---|
|
||||
| 无附件 | `prompt.trim()`;若也空则 `Err("聊天内容不能为空")` |
|
||||
| 附件都没有 `localPath` 且都没有 `status` | 保持现有 Home 文案与行格式,测试须逐字兼容 |
|
||||
| 任一条有 `localPath` 或 `status` | Project 头 + Project 行格式 |
|
||||
|
||||
Home 行(禁止改字):
|
||||
|
||||
```text
|
||||
[首页附件说明:当前尚未打开项目,以下仅为附件元数据,附件内容尚不可读取]
|
||||
- {name};类型:{mediaType};大小:{n} 字节
|
||||
```
|
||||
|
||||
Project 头与行(禁止出现 GDD / 规格 / 权威 / 必须读取):
|
||||
|
||||
```text
|
||||
<用户原文>
|
||||
|
||||
[本轮用户附件:已复制到当前项目。请用「项目路径」读取;原文件名不是磁盘路径。]
|
||||
- 原文件名:fast_gdd.md;项目路径:assets/uploads/upload-1788083777445-fast_gdd.md;类型:text/markdown;大小:7944 字节;状态:imported
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
- 条数上限仍为 8,超出写 `- 另有 N 个附件未展开`。
|
||||
- 名字清洗沿用 Home:basename、去掉控制字符、最多 160 字、空则「未命名附件」。
|
||||
- 媒体类型清洗沿用 Home。
|
||||
- `localPath` 只接受项目相对 POSIX 路径:无 `..`、无盘符/根路径、首段不是 `.agent` / `.git`,`\` 归一为 `/`,最长 512;不合法则该条不输出路径,状态按 `failed`。
|
||||
- 有附件时允许原文为空(Home 已如此)。Direct 内层若仍要求非空,在 command 边界先渲染再下传,避免空原文 + 有附件被拒。
|
||||
|
||||
不在 sidecar 里写「若用户要求按附件实施请先读取」。意图留在用户原文;做成游戏的固定 prompt 已经在说这件事。
|
||||
|
||||
### 3.3 谁渲染、谁看见
|
||||
|
||||
- sidecar **只在进 Codex 前由 Rust 拼装**。
|
||||
- `.agent/conversations/project.jsonl` 继续写用户原文(现有 `append_local_conversation_message`)。
|
||||
- 工作台气泡继续显示 latch / 输入框原文,不把 sidecar 画进 UI。
|
||||
- 未完成首轮的 hydration 重放目前只带 prompt:本期不把附件写进 jsonl,进程重启后的未完成首轮可能丢映射。完整成功首轮不受影响。不为此新增会话 schema。
|
||||
|
||||
## 4. 数据流
|
||||
|
||||
```text
|
||||
Home 上传 / 做成游戏 File
|
||||
→ upload_local_asset(已有)
|
||||
→ LauncherProjectContext.attachments(已有)
|
||||
→ ProjectSupervisor.initialAttachments
|
||||
→ 首轮 latch(与 prompt、creationType 同级)
|
||||
→ 仅 chat_with_game_creator_direct_codex.attachments
|
||||
→ render_direct_codex_user_prompt
|
||||
→ 现有 Direct turn(cwd = 项目根)
|
||||
```
|
||||
|
||||
Supervisor / 做方案首轮忽略 `attachments`,行为不变。
|
||||
|
||||
后续工作台手打消息不带 `attachments`。附件是这一轮带来的,不是项目终身上下文。
|
||||
|
||||
## 5. 代码落地(按最终结构,不按最小补丁)
|
||||
|
||||
### 5.1 Rust
|
||||
|
||||
新增 [`apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_attachments.rs`](../../apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_attachments.rs):
|
||||
|
||||
- DTO、清洗、上限常量、`render_direct_codex_user_prompt`
|
||||
- 单元测试(见第 6 节)
|
||||
|
||||
[`agent.rs`](../../apps/ai-game-creator-shell/src-tauri/src/agent.rs) 增加 `mod direct_codex_attachments`。
|
||||
|
||||
[`direct_runtime.rs`](../../apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime.rs):
|
||||
|
||||
- 删除 Home 专用 struct / sanitizer / `render_direct_codex_home_user_prompt`
|
||||
- `run_direct_game_creator_home_turn` 改为调用共享渲染
|
||||
- `chat_with_game_creator_direct_codex` 增加 `attachments: Option<Vec<DirectCodexTurnAttachment>>`,先渲染再调用现有 `run_direct_game_creator_turn_at_with_creation_type_and_emitter`
|
||||
- 把现有 Home 渲染测试迁到新文件;本文件不再保留一份平行实现
|
||||
|
||||
不要把 attachments 顺着 inner turn / emitter / CLI 往下传。CLI `run_direct_game_creator_turn_at` 不变。
|
||||
|
||||
### 5.2 前端
|
||||
|
||||
[`model.ts`](../../apps/ai-game-creator-shell/src/features/app-shell/model.ts) `ProjectSupervisorComponentProps` 增加:
|
||||
|
||||
```ts
|
||||
initialAttachments?: LauncherImportedAttachment[];
|
||||
```
|
||||
|
||||
[`WorkspaceLauncher.tsx`](../../apps/ai-game-creator-shell/src/features/app-shell/WorkspaceLauncher.tsx):
|
||||
|
||||
```ts
|
||||
initialAttachments={currentProjectContext.attachments}
|
||||
```
|
||||
|
||||
[`App.tsx`](../../apps/ai-game-creator-shell/src/App.tsx):
|
||||
|
||||
- props / latch 增加 `attachments`(默认 `[]`)
|
||||
- `executeChatAgentReply` 不要再加第 5 个位置参数,收成:
|
||||
|
||||
```ts
|
||||
{
|
||||
prompt: string;
|
||||
clientTurnId?: string;
|
||||
creationType?: HomeCreationType | null;
|
||||
attachments?: DirectCodexTurnAttachment[];
|
||||
}
|
||||
```
|
||||
|
||||
- Direct invoke:有 `creationType` 才写该字段(现有);`attachments?.length` 才写 `attachments`
|
||||
- Supervisor 分支完全不读 `attachments`
|
||||
- 现有 `executeChatAgentReply(prompt)` / 恢复未完成 turn 的调用改为对象形式,不传 attachments
|
||||
|
||||
瘦映射不要写在 1 万行的 `App.tsx` 里,放到例如 [`apps/ai-game-creator-shell/src/features/app-shell/directCodexTurnAttachments.ts`](../../apps/ai-game-creator-shell/src/features/app-shell/directCodexTurnAttachments.ts):`toDirectCodexTurnAttachments(imported)`,去掉 `error`,空 `localPath` 不输出该键。
|
||||
|
||||
[`useHomeProjectCreation.ts`](../../apps/ai-game-creator-shell/src/features/app-shell/useHomeProjectCreation.ts):`importHomeAttachments` 写入 `size: attachment.file.size`。不改 `initialPrompt` 兜底句,不改做成游戏(即便本分支尚未合入 PR #210,也不预埋 GDD 字段)。
|
||||
|
||||
[`types.ts`](../../apps/ai-game-creator-shell/src/app/types.ts):`LauncherImportedAttachment` 增加可选 `size?: number`。
|
||||
|
||||
### 5.3 文档
|
||||
|
||||
落地提交时(不是本方案文件自身):
|
||||
|
||||
- 本文件标为现行合同
|
||||
- `docs/README.md`、`docs/project-memory/shared-memory/document-map.md` 增加条目
|
||||
- `decision-log.md` 记一条:Direct 本轮附件只映射路径,不灌正文、不区别 GDD
|
||||
- 不把 issue #212 技术说明改写成「已修复」,等代码合入后再改状态
|
||||
|
||||
## 6. 测试
|
||||
|
||||
### Rust(新文件)
|
||||
|
||||
1. 无附件:原文 trim 后原样返回;空原文报错。
|
||||
2. Home 形态(无 path、无 status):与迁过来的两条现有测试逐字一致(含路径剥离、非法 mediaType、8 条上限)。
|
||||
3. Project 形态:原文保留;含原名与 `assets/uploads/...`;**断言不得出现「GDD」「规格」「权威」**。
|
||||
4. 同一列表里 png 与 md 行格式相同(只是 name/path/type 不同)。
|
||||
5. `status=failed` 且无 path:有状态、无项目路径、无 error 正文。
|
||||
6. 非法 `localPath`(`../`、`.agent/x`、绝对路径)不出现在输出中。
|
||||
7. 空原文 + 有附件:成功,且含 Project 头。
|
||||
|
||||
### 前端
|
||||
|
||||
1. [`home.suite.ts`](../../apps/ai-game-creator-shell/tests/appSurface/home.suite.ts)「imports home attachments…」:Direct invoke 必须带 `attachments`,其中 `name` 为 `角色参考.png`、`localPath` 为 upload 返回路径、`status: 'imported'`。用 png 证明不是 md 特例。
|
||||
2. 无附件的 Direct invoke 仍不得出现 `attachments` 键(或等价:不传该字段)。
|
||||
3. `planningStartMode` 首轮仍走 Supervisor,`chat_with_game_creator_agent` 的 payload 不含附件 sidecar。
|
||||
4. 工作台后发的普通消息:`chat_with_game_creator_direct_codex` 只有 `projectPath/prompt/clientTurnId`(及既有 creationType 规则),不带 attachments。
|
||||
5. 若本分支已能跑 PR #210 的 home.suite / plan-gdd 做成游戏用例:只断言它仍调用 `createHomeDraftAutomatically` / 仍使用原固定 prompt;**不要**给做成游戏加第二条附件协议。sidecar 由通用 Direct 断言覆盖。
|
||||
|
||||
### 不测
|
||||
|
||||
- 不把「必须生成弹幕射击」写成单测。
|
||||
- 不测 native `file.read` 是否进 `agent.db`。
|
||||
|
||||
## 7. 做成游戏为什么不用改
|
||||
|
||||
PR #210 `startGameFromApprovedGdd`:
|
||||
|
||||
1. 读策划项目 `game/fast_gdd.md`
|
||||
2. `new File([content], 'fast_gdd.md')`
|
||||
3. `createHomeDraftAutomatically({ creationType: 'game', prompt: APPROVED_GDD_BUILD_PROMPT, attachments }, 'direct-build')`
|
||||
|
||||
之后与首页拖一个 md 完全相同。sidecar 见到的是「原文件名 `fast_gdd.md` + 新项目 `assets/uploads/upload-…-fast_gdd.md`」。固定 prompt 继续说「读附件中的 fast_gdd.md」,映射补上真实路径。
|
||||
|
||||
## 8. 验收
|
||||
|
||||
1. 首页做游戏:上传任意文本或图片 + 一句话,Direct 首轮 prompt 含原名和 `assets/uploads/...`。
|
||||
2. 做成游戏(PR #210 合入后或该分支上):固定 prompt 一字不改,同时出现改写后的项目路径。
|
||||
3. 做方案首轮:Supervisor 行为与现在一致,无 sidecar。
|
||||
4. 无附件:Direct 入参与现在一致。
|
||||
5. `npm run check:encoding`、`git diff --check`、相关 `home.suite` / Direct Rust 测试通过。
|
||||
|
||||
## 9. 实现顺序
|
||||
|
||||
1. Rust 共享渲染 + 迁 Home 测试 + Direct command 接 `attachments`
|
||||
2. 前端 latch / invoke / 映射 / `size`
|
||||
3. 改 `home.suite` 附件断言
|
||||
4. encoding 与定向测试
|
||||
5. 合入时补 decision-log 与文档索引
|
||||
@@ -0,0 +1,389 @@
|
||||
# Direct 回合行为审计账本
|
||||
|
||||
- 日期:2026-08-31
|
||||
- 状态:现行合同(已按本文落地)
|
||||
- 问题:Gitea issue #212 的第二段(Direct 原生读 / 工具行为无法从项目产物判断);用于分析「附件已映射仍未按文档实施」
|
||||
- 关联:[`【技术方案】DirectProject本轮附件路径映射-2026-08-31.md`](./【技术方案】DirectProject本轮附件路径映射-2026-08-31.md)、[`【技术说明】DirectProject未消费用户上传权威文档-2026-08-30.md`](./【技术说明】DirectProject未消费用户上传权威文档-2026-08-30.md)
|
||||
- 原则:落地后代码简洁可维护;审计是 Direct 行为时间线,不是 GDD 特例,也不替代 sidecar
|
||||
|
||||
## 0. 一句话
|
||||
|
||||
Direct GUI 回合已经能看见 Codex `item/completed`,但只收成 UI 活动词,隔离 `CODEX_HOME` 随后删除。本方案在项目内留下有界、可共享的回合账本:本轮提供了哪些附件路径、按什么顺序做了读/搜/列表/MCP/写文件,以及第一次定玩法的动作是什么。用来区分「没读附件」和「读了仍走默认收集类」,不灌正文、不强制读取、不拷会话目录。
|
||||
|
||||
## 1. 目标与非目标
|
||||
|
||||
### 目标
|
||||
|
||||
一次带 `clientTurnId` 的 DirectProject GUI 回合结束后,只凭项目目录应能回答:
|
||||
|
||||
1. 本轮 sidecar 是否发出,原名映射到哪些项目相对路径,文件当时的 `contentSha256`。
|
||||
2. 模型是否用 native 命令 / 列表 / 搜索 / 看图 / MCP 打开过那些路径(路径 + 当时磁盘 hash,不是 stdout)。
|
||||
3. **顺序**:读附件是在第一次美术 brief / 第一次写 `game/` 之前还是之后。
|
||||
4. 第一次「定玩法」动作是什么(优先 `taonier_prepare_game_art.brief`,否则其它生成类 MCP 或对 `game/` 的写入)。
|
||||
|
||||
覆盖后续手打回合:只要 GUI Direct 有 `clientTurnId` 就记账本,附件可以为空。
|
||||
|
||||
### 非目标
|
||||
|
||||
- 不证明「理解并按 GDD 实施」。那是对照 `game/index.html`、美术产物做的产品判断;账本只提供行为时间线。
|
||||
- 不把 issue #212 原文的 NLP / 「关键内容进入上下文」做成自动判决。
|
||||
- 不灌附件正文进 prompt,不强制先读再继续,不为 GDD 开协议特例。
|
||||
- 不改 sidecar 文案、jsonl 用户原文、工作台气泡。
|
||||
- 不改做成游戏固定 prompt / PR #210 注入。
|
||||
- 不复用 Supervisor `agent.runtime.action_receipt` / `file.read`。
|
||||
- 不拷隔离 `CODEX_HOME`、不落 `auth.json`、不落 `aggregated_output` / MCP `result` / patch `diff` / `FunctionCallOutput` 正文。
|
||||
- 不把原始 item JSON 送进 Tauri 前端事件(现有 `DirectCodexTurnObservation` 仍只允许安全活动词和流式正文)。
|
||||
- 不扫 `kind=uploaded` 历史附件;只记本轮 sidecar 提供的集合。
|
||||
- DirectHome、ToolHost、CLI `--direct-codex-chat`(无 `clientTurnId`)本期不写这份账本。
|
||||
- 本期不改 UI,不在聊天面板展示审计。
|
||||
- 不把 issue #212 标成已修复;sidecar 与本账本是两段工作。
|
||||
|
||||
## 2. 现状
|
||||
|
||||
```text
|
||||
Codex item/completed
|
||||
commandExecution / mcpToolCall / fileChange / imageView / …
|
||||
│
|
||||
├─ 现用:收成 Activity("validation"|"controlled-tool"|…)
|
||||
│ → Tauri 进度,不落盘
|
||||
└─ 不用:隔离 CODEX_HOME session(含 stdout)→ tempdir Drop 删除
|
||||
```
|
||||
|
||||
项目里现有:
|
||||
|
||||
| 产物 | 记下的 | 缺的 |
|
||||
|---|---|---|
|
||||
| `.agent/conversations/project.jsonl` | 用户原文 + 助手终稿 | sidecar、工具调用 |
|
||||
| `.agent/agent.db` | init / upload / 美术登记 / 对话指针 | native 读、MCP 调用、`agc_write_file` |
|
||||
| `.agent/logs/command.log` | 权限确认 | 原生命令 |
|
||||
| `asset.register` / `canvas.asset_generate` | 路径、切片、部分 `source.prompt` | 与读附件的先后 |
|
||||
| 隔离 `CODEX_HOME` | Codex 自己的 session | 回合结束即删 |
|
||||
|
||||
Codex app-server 协议里,`commandExecution.commandActions` 已分类为 `Read | ListFiles | Search | Unknown`,`Read.path` 在协议侧会拼成 cwd 绝对路径。Direct cwd 就是项目根(`resolve_direct_codex_project_authority` 不再强制 `game/` 子目录)。抽取时把绝对路径收回项目相对 POSIX,失败则丢路径,不写宿主绝对路径。
|
||||
|
||||
`agc_write_file` 经 tool bridge 落盘,当前不写 `agent.db`。不给每个 MCP 单独打点;统一在 `item/completed` 抽一次。
|
||||
|
||||
## 3. 分析用判据(相对 issue 收窄)
|
||||
|
||||
落地后,对类似 `gameagent-9baa5293` 的 run,应能三分:
|
||||
|
||||
| 时间线 | 结论 | 下一刀不该打哪 |
|
||||
|---|---|---|
|
||||
| `offeredRead.read=false`,`firstDesign` 已是 `taonier_prepare_game_art` 且 brief 是收集类 | 没打开附件就定了玩法 | 不是「GDD 解析不够」 |
|
||||
| 先 `Read` 且 hash 对上,brief 仍是收集类 | 读了但没用 | sidecar 已够;看四切片 / icon-spec「收集物」/ 完成合同 |
|
||||
| 只有 `ListFiles` / `Search` 命中 uploads,没有 `Read` | 发现了没读正文 | 映射可能够,缺的是读 |
|
||||
| `Read` 的 path 是 `fast_gdd.md` 而不是 `assets/uploads/…` | sidecar 没被当成磁盘路径 | 还是路径合同 |
|
||||
|
||||
不在账本里写「已遵循 GDD」或「未遵循 GDD」布尔。
|
||||
|
||||
## 4. 落点
|
||||
|
||||
两层,都在项目 `.agent/` 控制面内,模型读不到:
|
||||
|
||||
1. **权威时间线**(每回合一个 jsonl,只追加)
|
||||
`.agent/runtime/direct-codex/turns/<clientTurnId>.jsonl`
|
||||
2. **总索引一条摘要**(方便继续翻现有 `agent.db`)
|
||||
`recordType: "direct.codex.turn"`
|
||||
|
||||
`clientTurnId` 沿用现有规则:trim 后 6–160 位 ASCII 字母数字或连字符,首位字母或数字。文件名用规范化后的 id,不再二次编码。
|
||||
|
||||
不升级 `GAME_CREATOR_AGENT_DB_SCHEMA_VERSION`;新 `recordType` 走 Ordinary 追加。`updatedAt` / `schemaVersion` 仍由 `serialize_agent_db_record` 写入。
|
||||
|
||||
jsonl 每条自带 `recordedAtMs`(`unix_millis`)。同一 `clientTurnId` 若再次进入(当前 GUI 运行中互斥,结束后理论上可再来):只追加,不截断;后一次 `turn_start` 视为新 attempt。读摘要时按文件内最后一次 `turn_start` 到对应 `turn_end` 计算 `offeredRead`。`agent.db` 每次 `turn_end` 再追加一条摘要,分析取该 `clientTurnId` 最后一条。
|
||||
|
||||
## 5. 记录合同
|
||||
|
||||
camelCase JSON。禁止出现附件正文、命令 stdout、patch diff、宿主绝对路径、Token、URL 签名。
|
||||
|
||||
### 5.1 `turn_start`
|
||||
|
||||
在 sidecar **已经渲染之后**、Codex turn **启动之前**写入。`promptSha256` 哈希的是 **用户原文**(command 入参 `prompt`),不是带 sidecar 的全文。
|
||||
|
||||
```json
|
||||
{
|
||||
"recordType": "direct.codex.turn_start",
|
||||
"clientTurnId": "Abc123-def",
|
||||
"sidecarPresent": true,
|
||||
"promptSha256": "<sha256 hex of original user text UTF-8>",
|
||||
"promptChars": 120,
|
||||
"attachments": [
|
||||
{
|
||||
"name": "fast_gdd.md",
|
||||
"localPath": "assets/uploads/upload-1788164530559-fast_gdd.md",
|
||||
"mediaType": "text/markdown",
|
||||
"size": 8119,
|
||||
"status": "imported",
|
||||
"contentSha256": "<sha256 hex or omit>",
|
||||
"hashSkipped": null
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- `attachments` 清洗复用 sidecar:`sanitize_attachment_name` / `media_type` / `status` / `local_path`。把这些函数改成 `pub(crate)`,审计模块不要复制一份。
|
||||
- 条数上限仍 8;超出只在 sidecar 文案里写「另有 N 个未展开」,账本 `attachments` 同样只留前 8,另加 `attachmentsOmitted: N`。
|
||||
- `sidecarPresent`:本轮渲染走了 Project 头(任一条有合法 path 或 status)。Home 形态不会出现在本账本(Home 不记账)。
|
||||
- `contentSha256`:对清洗后的 `localPath` 读项目文件做 SHA-256 小写 hex。文件不存在则省略 hash,`hashSkipped: "missing"`。超过 `DIRECT_CODEX_AUDIT_HASH_MAX_BYTES`(2 MiB)则 `hashSkipped: "too-large"`。`.agent` / `.git` / `..` 路径本来就不会出现在 sidecar 输出里。
|
||||
|
||||
无附件:`attachments: []`,`sidecarPresent: false`,仍然写 `turn_start`。
|
||||
|
||||
### 5.2 `item`
|
||||
|
||||
仅 `item/completed`。`item/started` 和 `outputDelta` 不落盘。
|
||||
|
||||
公共字段:
|
||||
|
||||
```json
|
||||
{
|
||||
"recordType": "direct.codex.item",
|
||||
"clientTurnId": "Abc123-def",
|
||||
"seq": 1,
|
||||
"itemId": "item-…",
|
||||
"itemType": "commandExecution",
|
||||
"status": "completed"
|
||||
}
|
||||
```
|
||||
|
||||
`seq` 从 1 起,按成功写入的 item 递增。`itemType` 取 Codex `item.type` 原词;未知类型仍记账 `itemType`,不附带未清洗 payload。
|
||||
|
||||
按类型附加字段:
|
||||
|
||||
| `item.type` | 追加 | 禁止 |
|
||||
|---|---|---|
|
||||
| `commandExecution` | `command` 截断 240 字;`exitCode`;`durationMs`;`actions[]` | `aggregatedOutput` |
|
||||
| `mcpToolCall` | `tool`、`server`(可省略默认 `agc_tools`)、`durationMs`、§5.4 参数 | `result`、`error` 原文(只留 `status` / `errorKind`) |
|
||||
| `fileChange` | `changes: [{ path, kind }]`,`kind` 为 `add` / `delete` / `update` | `diff`、`movePath` 的宿主绝对路径(相对化失败则整条 change 丢 path) |
|
||||
| `imageView` | `path` | 图像字节 |
|
||||
| `functionCallOutput` | `name`、`namespace` | `output` |
|
||||
| `webSearch` | `query` 截断 400 字 | 结果页正文 |
|
||||
| `agentMessage` / `userMessage` / `plan` / `reasoning` / `contextCompaction` / `hookPrompt` | **整类跳过**(终稿已在 jsonl;推理正文不是本账本) | — |
|
||||
| 其它未知 | 只留公共字段 | 原始 `item` 对象 |
|
||||
|
||||
`commandExecution.actions[]`:
|
||||
|
||||
```json
|
||||
{ "type": "read", "path": "assets/uploads/upload-…-fast_gdd.md", "contentSha256": "…", "hashSkipped": null }
|
||||
{ "type": "listFiles", "path": "assets" }
|
||||
{ "type": "search", "query": "fast_gdd", "path": null }
|
||||
{ "type": "unknown" }
|
||||
```
|
||||
|
||||
- `Read.path` 先相对化再清洗;失败则该 action 记 `{ "type": "read", "pathRejected": true }`,不写绝对路径。
|
||||
- 相对化成功后,对磁盘文件按 §5.1 同一套 hash 规则补 `contentSha256`。
|
||||
- `command` 里若相对化失败,把 `command` 整段丢掉,改 `commandRedacted: true`(避免 `type C:\Users\…\fast_gdd.md` 进账本)。
|
||||
|
||||
### 5.3 MCP 参数白名单
|
||||
|
||||
只抄这些键,其它键丢弃。字符串再经 path 清洗或截断。
|
||||
|
||||
| 工具 | 落盘参数 | 正文类字段 |
|
||||
|---|---|---|
|
||||
| `agc_list_project_files` | `path`、`query`(120)、`kind`、`offset`、`limit` | 无 |
|
||||
| `agc_write_file` | `path`、`contentChars`(`content` 的字符数,不是正文) | 不落 `content` |
|
||||
| `taonier_prepare_game_art` | `mode`、`brief`(截断 4000)、`briefChars`、`briefSha256` | **要 brief 原文**(分析定玩法的吸烟枪;上限已是 MCP 合同) |
|
||||
| `agc_generate_image` | `kind`、`aspectRatio`、`imageSize`、`assetName`、`outputPath`、`prompt` 截断 4000、`promptChars`、`promptSha256` | 不落 32k 全文 |
|
||||
| `agc_edit_image` | `sourceLocalAssetId`、`assetName`、`prompt` 截断 4000、`promptChars`、`promptSha256` | 同上 |
|
||||
| `agc_create_or_derive_resource` | `kind`、`mode`、`sourceLocalAssetId`、`assetName`、`prompt` 截断 4000、`promptChars`、`promptSha256` | MCP 上限已是 4000 |
|
||||
| `agc_list_registered_assets` | `kind`、`assetId`、`includeSequenceFrames`、`offset`、`limit` | 无 |
|
||||
| `agc_list_account_assets` | `folderId`、`query`、`offset`、`limit` | 无 |
|
||||
| `agc_import_account_assets` | `assetIds`(最多 8 个 id,超出 `assetIdsOmitted`)、`localPaths`(清洗后相对路径,最多 8) | 无 |
|
||||
| `agc_remove_background` | `sourceLocalAssetId`、`assetName` | 无 |
|
||||
| `agc_browser_playtest` | `attempt` | 无 |
|
||||
| `agc_web_search` | `query` 截断 400、`maxResults` | 无 |
|
||||
| `agc_read_skill_resource` | `skillName`、`relativePath` | 不落 Skill 正文 |
|
||||
| 未知 MCP 名 | 只留 `tool` + `status` | 不落 `arguments` |
|
||||
|
||||
`brief` / 截断后的 `prompt` 是 **模型自己写的设计文本**,不是用户 GDD 转储。这是分析「仍走收集类」的关键,允许进 jsonl。`agent.db` 摘要只留 `briefPreview` 240 字。
|
||||
|
||||
### 5.4 `turn_end`
|
||||
|
||||
派生摘要,不是第二真相。字段必须能从本文件已写入的 `turn_start` + `item` 重算出来。
|
||||
|
||||
```json
|
||||
{
|
||||
"recordType": "direct.codex.turn_end",
|
||||
"clientTurnId": "Abc123-def",
|
||||
"completed": true,
|
||||
"itemCount": 17,
|
||||
"itemsTruncated": false,
|
||||
"offeredRead": [
|
||||
{
|
||||
"localPath": "assets/uploads/upload-1788164530559-fast_gdd.md",
|
||||
"read": false
|
||||
}
|
||||
],
|
||||
"firstDesign": {
|
||||
"kind": "mcp:taonier_prepare_game_art",
|
||||
"seq": 3,
|
||||
"tool": "taonier_prepare_game_art",
|
||||
"briefPreview": "俯视角收集冒险小游戏…"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`offeredRead.read=true` 当且仅当本 attempt 内存在 `actions.type=read` 或 `imageView` 或 MCP 参数里的 `path` / `localPaths`,清洗后与 `localPath` 字符串相等。hash 对不上仍记 `read: true`,另加 `contentSha256Match: false`(读了另一份同路径文件或读时文件已变)。没有 hash 可对则省略 `contentSha256Match`。
|
||||
|
||||
`firstDesign`:本 attempt 第一条满足任一条件的 item:
|
||||
|
||||
1. MCP:`taonier_prepare_game_art` / `agc_generate_image` / `agc_edit_image` / `agc_create_or_derive_resource`
|
||||
2. `agc_write_file` 且 path 以 `game/` 开头或文件名是 `index.html`
|
||||
3. `fileChange` 且任一条 change path 满足 2
|
||||
|
||||
列表、搜索、读、Skill 读取、账户素材查询、playtest、web_search **不算** firstDesign。没有则 `firstDesign: null`。
|
||||
|
||||
`kind` 取值:`mcp:<tool>` / `write:<path>` / `fileChange:<path>`。
|
||||
|
||||
### 5.5 `agent.db` 摘要
|
||||
|
||||
```json
|
||||
{
|
||||
"recordType": "direct.codex.turn",
|
||||
"clientTurnId": "Abc123-def",
|
||||
"turnLog": ".agent/runtime/direct-codex/turns/Abc123-def.jsonl",
|
||||
"sidecarPresent": true,
|
||||
"offeredCount": 1,
|
||||
"offeredRead": [ { "localPath": "assets/uploads/…-fast_gdd.md", "read": false } ],
|
||||
"firstDesign": { "kind": "mcp:taonier_prepare_game_art", "seq": 3, "briefPreview": "…" },
|
||||
"itemCount": 17,
|
||||
"itemsTruncated": false,
|
||||
"completed": true,
|
||||
"auditWriteFailed": false
|
||||
}
|
||||
```
|
||||
|
||||
`turnLog` 必须是项目相对 POSIX。不要把 jsonl 全文复制进 `agent.db`。单条仍受 Ordinary 1 MiB 限制;摘要本身应远小于此。
|
||||
|
||||
### 5.6 上限
|
||||
|
||||
| 项 | 值 |
|
||||
|---|---|
|
||||
| 每回合 item 条数 | 256;超出再写一条 `recordType: "direct.codex.items_truncated"`,之后 item 丢弃但仍把 `turn_end.itemsTruncated=true` |
|
||||
| `command` | 240 字 |
|
||||
| `brief` / 生成类 `prompt` 落盘 | 4000 字 |
|
||||
| `briefPreview` | 240 字 |
|
||||
| 文件 hash | 2 MiB |
|
||||
| 附件条数 | 8(与 sidecar 相同) |
|
||||
| jsonl 单行 | 沿用现有 jsonl 追加上限;超长截断正文类字段,不截断结构 |
|
||||
|
||||
## 6. 调用链
|
||||
|
||||
```text
|
||||
chat_with_game_creator_direct_codex
|
||||
规范化 clientTurnId
|
||||
DirectCodexTurnAudit::start(root, clientTurnId, originalPrompt, attachments)
|
||||
→ 写 turn_start(fail-open)
|
||||
render_direct_codex_user_prompt // 现有 sidecar,不变
|
||||
run_direct_game_creator_turn_at_with_creation_type_and_emitter(..., audit)
|
||||
→ Codex collect 循环在 DirectProject + item/completed 调 audit.observe_item
|
||||
Ok/Err 都 audit.finish(completed)
|
||||
→ 写 turn_end + agent.db 摘要
|
||||
```
|
||||
|
||||
- CLI `run_direct_game_creator_turn_at` **不** 接 audit(无 `clientTurnId`)。
|
||||
- Home command 不接 audit。
|
||||
- 不要把 attachments / audit 顺着 CLI inner、pool、ToolHost 往下传。
|
||||
- `DirectCodexTurnObservation` **不** 增加原始 `params`。审计走独立 `DirectCodexTurnAudit`,避免 stdout 正文进入 Tauri 事件。
|
||||
|
||||
`direct_game_creator_codex_chat_at_with_optional_observer` 增加可选 `audit: Option<&mut DirectCodexTurnAudit>`,再传到 `run_turn_with_direct_observer`。仅 `workspace_mode == DirectProject` 且 `audit` 为 Some 时抽取。
|
||||
|
||||
`run_direct_game_creator_turn_inner` 的 UI observer 保持只处理 `AccumulatedText` / `Activity`。
|
||||
|
||||
回合失败(生成失败、浏览器试玩失败、回复落盘失败):只要 `start` 过就 `finish(false)`,保留已观察到的 item。Codex 尚未启动则 `itemCount=0`。
|
||||
|
||||
## 7. 失败语义
|
||||
|
||||
审计 **不得** 把做游戏打失败。所有写盘包在 sink 内:
|
||||
|
||||
- 单次追加失败:记内存 `audit_write_failed=true`,后续 item 仍尝试写;`finish` 时摘要带 `auditWriteFailed: true`。
|
||||
- 连摘要都写不进去:只在 Direct debug / 现有进度通道能承受的前提下忽略;不新增用户可见报错文案。
|
||||
- 不引入新的 Tauri 事件名。
|
||||
|
||||
与 `conversation.write` 失败不同:助手终稿落盘失败仍按现有逻辑拒绝返回。审计失败不走那条。
|
||||
|
||||
## 8. 安全
|
||||
|
||||
- 路径:与 sidecar 同一套相对 POSIX 清洗;相对化失败不写原绝对路径。
|
||||
- 控制面:hash / 读文件只用 `resolve_local_project_path`;拒绝 `.agent` / `.git` / 敏感文件。这些路径若出现在 commandActions 里,只记 `pathRejected`。
|
||||
- 不把 `aggregated_output`、MCP result、function output、diff 暂存在内存再截断——抽取函数根本不读这些键。
|
||||
- jsonl 位于 `.agent/runtime/**`,现有 Direct 控制面边界禁止模型当普通项目文档读。
|
||||
- 前端观察者和审计 sink 分叉,禁止图省事 `observer(Item { params })`。
|
||||
|
||||
## 9. 代码落地
|
||||
|
||||
新增 [`apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_audit.rs`](../../apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_audit.rs):
|
||||
|
||||
- `DirectCodexTurnAudit`
|
||||
- `start` / `observe_item` / `finish`
|
||||
- 相对化、hash、MCP 白名单、`firstDesign` / `offeredRead` 派生
|
||||
- 单元测试(见 §11)
|
||||
|
||||
[`agent.rs`](../../apps/ai-game-creator-shell/src-tauri/src/agent.rs):`mod direct_codex_audit` + `pub(crate) use`。
|
||||
|
||||
[`direct_codex_attachments.rs`](../../apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_attachments.rs):清洗函数改 `pub(crate)`,行为不变。
|
||||
|
||||
[`codex_app_server.rs`](../../apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server.rs):
|
||||
|
||||
- `run_turn_with_direct_observer` / `direct_game_creator_codex_chat_at_with_optional_observer` 增加 `audit: Option<&mut DirectCodexTurnAudit>`
|
||||
- collect 循环 `Item { completed: true, .. }` 且 DirectProject 时 `audit.observe_item(¶ms)`
|
||||
- **不要** 把 `params` 塞进 `DirectCodexTurnObservation`
|
||||
- 现有 Home / ToolHost / 安全活动词测试保持逐字
|
||||
|
||||
[`direct_runtime.rs`](../../apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime.rs):
|
||||
|
||||
- `chat_with_game_creator_direct_codex` 创建 audit(原文 + attachments),Ok/Err 都 `finish`
|
||||
- 把 audit 传入 `run_direct_game_creator_turn_at_with_creation_type_and_emitter`
|
||||
- 该函数和 inner 增加可选 audit;CLI 入口签名不变
|
||||
|
||||
不要给 `agc_write_file` / 每个 MCP handler 再写一份平行审计。
|
||||
|
||||
前端、DTO、sidecar 文案、home.suite 附件断言:本期不改。不新增 UI。
|
||||
|
||||
文档:落地提交时把本文状态改为「现行合同(已按本文落地)」;`decision-log.md` 记一条;不要把 08-30 技术说明改成已修复。
|
||||
|
||||
## 10. 测试
|
||||
|
||||
全部是 Rust 单元测试,用 fixture item JSON,不拉真 Codex。
|
||||
|
||||
1. `turn_start`:原文 hash 稳定;sidecar 路径清洗后出现;非法 `../` 不进 attachments;无附件 `sidecarPresent=false`。
|
||||
2. 附件文件写入临时项目后 `contentSha256` 与直接 hash 一致;缺文件 `hashSkipped=missing`;超过 2 MiB `too-large`。
|
||||
3. `commandExecution` + `commandActions: [{type:read, path: <abs>}]` → 相对路径 + hash;`aggregatedOutput` 即使在 fixture 里也不出现在落盘 JSON。
|
||||
4. `Read` 相对化失败 → `pathRejected`,落盘 JSON 不含 `C:\\` / `Users`。
|
||||
5. `mcpToolCall` `taonier_prepare_game_art`:`brief` 保留;`result` 丢掉。
|
||||
6. `agc_write_file`:有 path 与 `contentChars`,无 content。
|
||||
7. `agc_generate_image`:32k prompt 只留 4000 + `promptChars` + sha256。
|
||||
8. `fileChange`:path + kind,无 diff。
|
||||
9. `offeredRead`:读路径等于 offered → `read=true`;只 list/search → `read=false`。
|
||||
10. `firstDesign`:先 read 再 art → kind 是 mcp art,seq 是 art 那条;只有 read → `null`。
|
||||
11. 第 257 条 item 触发 truncated,`turn_end.itemsTruncated=true`。
|
||||
12. `agent.db` 摘要含 `turnLog` 相对路径、`offeredRead`、`firstDesign.briefPreview`。
|
||||
13. 写盘注入失败:`finish` 不 panic、不返回 Err 给调用方(sink 方法是 `()`)。
|
||||
14. 未知 `item.type` 只留公共字段。
|
||||
15. 现有 DirectHome 活动词测试、sidecar 渲染测试不受影响。
|
||||
|
||||
不测:真模型是否读 GDD、是否生成弹幕射击、浏览器验收文案。
|
||||
|
||||
## 11. 验收(方案落地后的人工分析)
|
||||
|
||||
用一次「上传 md + 做成游戏 / 首页做游戏」的本地项目:
|
||||
|
||||
1. 存在 `.agent/runtime/direct-codex/turns/<clientTurnId>.jsonl`。
|
||||
2. `agent.db` 有对应 `direct.codex.turn`。
|
||||
3. `turn_start.attachments[].localPath` 与 sidecar 项目路径一致。
|
||||
4. jsonl **没有** GDD 正文、没有 `aggregatedOutput`、没有 patch。
|
||||
5. 能根据 `offeredRead` + `firstDesign` 填上 §3 四行表的其中一行,而不用猜隔离 session。
|
||||
6. 工作台气泡仍是用户原文;jsonl 对话仍无 sidecar。
|
||||
7. `npm run check:encoding`、`git diff --check`、相关 Rust 单测通过。
|
||||
|
||||
## 12. 实现顺序
|
||||
|
||||
1. `direct_codex_audit.rs` + 清洗函数 `pub(crate)` + fixture 测试
|
||||
2. `codex_app_server` collect 接 sink;观察者枚举不变
|
||||
3. GUI Direct command 创建 / finish sink
|
||||
4. encoding 与定向 `cargo test`
|
||||
5. 合入时改本文状态、decision-log、sidecar 文档里「native 审计另排期」那一行改为指向本文
|
||||
|
||||
## 13. 与 sidecar / issue 的边界
|
||||
|
||||
- sidecar:让模型 **知道路径**。已落地,合同不变。
|
||||
- 本账本:让人 **看见模型做了什么**。不替代 sidecar,也不在本方案里做强制读取。
|
||||
- issue #212 主问题仍是消费失败;本账本是为下一刀修复提供证据,不是把 212 关单。
|
||||
@@ -206,6 +206,7 @@ Direct app-server 使用 ephemeral thread。stdout 中的 `item/started`、`item
|
||||
- 策划 run ID:`gameagent-cd7f6c81`
|
||||
- 做游戏 run ID:`gameagent-77b5aa31`
|
||||
- 上传 GDD(项目相对路径):`assets/uploads/upload-1788083777445-fast_gdd.md`
|
||||
- 审计账本方案(已落地,不关闭本 Issue):[`【技术方案】Direct回合行为审计账本-2026-08-31.md`](./【技术方案】Direct回合行为审计账本-2026-08-31.md)
|
||||
- 建议随 Issue 附上或引用对应 run 的以下复核材料:
|
||||
- Direct 对话:`.agent/conversations/project.jsonl`
|
||||
- Direct Agent DB:`.agent/agent.db`
|
||||
|
||||
Reference in New Issue
Block a user