diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_user_item/model.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_user_item/model.rs index e739b420b..dc20fb9e7 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_user_item/model.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_user_item/model.rs @@ -39,8 +39,6 @@ pub(crate) enum DirectCodexUserContentPart { /// Uploaded project attachment kept inline in canonical content. #[serde(rename = "agc_attachment_reference")] AgcAttachmentReference(DirectCodexUserAttachmentReferencePart), - #[serde(rename = "agc_image_reference")] - AgcImageReference(DirectCodexUserAttachmentReferencePart), } #[derive(Clone, Debug, Deserialize, Serialize, TS)] diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_user_item/validation.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_user_item/validation.rs index 50c285729..dfe3dda4d 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_user_item/validation.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_user_item/validation.rs @@ -40,8 +40,7 @@ pub(crate) fn validate_direct_codex_user_item( reference_count = reference_count.saturating_add(1); validate_runtime_region_reference(&manifest, reference)?; } - DirectCodexUserContentPart::AgcAttachmentReference(reference) - | DirectCodexUserContentPart::AgcImageReference(reference) => { + DirectCodexUserContentPart::AgcAttachmentReference(reference) => { if reference.name.trim().is_empty() { return Err("附件缺少文件名".to_string()); } diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_user_item/wire.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_user_item/wire.rs index c4352f0cf..9c43406e6 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_user_item/wire.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_codex_user_item/wire.rs @@ -100,8 +100,7 @@ pub(crate) fn direct_codex_user_item_to_wire_input( summary.push(']'); summary } - DirectCodexUserContentPart::AgcAttachmentReference(reference) - | DirectCodexUserContentPart::AgcImageReference(reference) => { + DirectCodexUserContentPart::AgcAttachmentReference(reference) => { let mut summary = format!( "[附件:名称={};类型={};大小={} 字节", reference.name.trim(), @@ -191,7 +190,7 @@ mod tests { } #[test] - fn attachment_and_image_parts_remain_in_canonical_order_when_projected() { + fn attachment_parts_remain_in_canonical_order_when_projected() { let root = tempfile::tempdir().expect("temp project"); crate::init_local_game_project_at(root.path(), "wire-test", "wire 投影测试") .expect("init project"); @@ -201,16 +200,14 @@ mod tests { "id": "turn-1:user", "content": [ {"type": "input_text", "text": "先看"}, - {"type": "agc_image_reference", "name": "hero.png", "mediaType": "image/png", "size": 12, "localPath": "assets/hero.png", "status": "imported"}, {"type": "agc_attachment_reference", "name": "notes.txt", "mediaType": "text/plain", "size": 4, "localPath": "assets/notes.txt", "status": "imported"} ] }); let projected = direct_codex_user_item_to_response_item(root.path(), &item) .expect("user response item should project"); let content = projected["content"].as_array().expect("content array"); - assert_eq!(content.len(), 3); + assert_eq!(content.len(), 2); assert!(content[0]["text"].as_str().unwrap().contains("先看")); - assert!(content[1]["text"].as_str().unwrap().contains("hero.png")); - assert!(content[2]["text"].as_str().unwrap().contains("notes.txt")); + assert!(content[1]["text"].as_str().unwrap().contains("notes.txt")); } } diff --git a/apps/ai-game-creator-shell/src/features/project-workspace/generated/DirectCodexUserContentPart.ts b/apps/ai-game-creator-shell/src/features/project-workspace/generated/DirectCodexUserContentPart.ts index c3b7b0ea1..da509125d 100644 --- a/apps/ai-game-creator-shell/src/features/project-workspace/generated/DirectCodexUserContentPart.ts +++ b/apps/ai-game-creator-shell/src/features/project-workspace/generated/DirectCodexUserContentPart.ts @@ -10,5 +10,4 @@ export type DirectCodexUserContentPart = } & DirectCodexUserRuntimeRegionPart) | ({ type: 'agc_attachment_reference'; - } & DirectCodexUserAttachmentReferencePart) - | ({ type: 'agc_image_reference' } & DirectCodexUserAttachmentReferencePart); + } & DirectCodexUserAttachmentReferencePart); diff --git a/docs/project-memory/plans/【实施计划】DirectProject用户ResponseItem输入-2026-09-15.md b/docs/project-memory/plans/【实施计划】DirectProject用户ResponseItem输入-2026-09-15.md index 51b9ea7b8..c18716d77 100644 --- a/docs/project-memory/plans/【实施计划】DirectProject用户ResponseItem输入-2026-09-15.md +++ b/docs/project-memory/plans/【实施计划】DirectProject用户ResponseItem输入-2026-09-15.md @@ -10,6 +10,7 @@ - 允许修改:AGC 壳 Rust agent 输入合同、DirectProject 历史适配、前端聊天引用模型、ts-rs 生成配置、当前聊天素材文档。 - 明确不修改:assistant 返回协议、工具 activity、附件/图片上传 DTO、SpacetimeDB、HTTP API;上传 DTO 仅在入口转换为 canonical `content[]`,不再以 sidecar 文本追加。 +- 本切片明确:不根据 MIME 类型判断图片,不生成 `agc_image_reference`;所有上传文件统一映射为 `agc_attachment_reference`。 ## 实现顺序 diff --git a/docs/project-memory/plans/【里程碑】DirectProject用户ResponseItem输入-2026-09-15.md b/docs/project-memory/plans/【里程碑】DirectProject用户ResponseItem输入-2026-09-15.md index 963ad4791..7a36a3be7 100644 --- a/docs/project-memory/plans/【里程碑】DirectProject用户ResponseItem输入-2026-09-15.md +++ b/docs/project-memory/plans/【里程碑】DirectProject用户ResponseItem输入-2026-09-15.md @@ -19,13 +19,13 @@ - `agc_runtime_region_reference` 保留运行区域语义摘要。 - Rust 在持久化前完成白名单、manifest 与路径校验。 - 现有标准 `response_item` 原样兼容;legacy conversation 行不提供 fallback。 -- 保持 assistant 返回、工具 activity 与上传 DTO 不变;但 DirectProject canonical user item 不再保存附件 sidecar,附件和图片必须作为 `content[]` 中按顺序排列的 `agc_attachment_reference` / `agc_image_reference`。 +- 保持 assistant 返回、工具 activity 与上传 DTO 不变;DirectProject canonical user item 不再保存附件 sidecar,所有本地上传文件(包括图片)统一作为 `content[]` 中按顺序排列的 `agc_attachment_reference`。 ## 不在范围内 - assistant item 前端投影或 Tauri 返回值改造。 - 工具 item、reasoning、file change、MCP item 的 UI 模型化。 -- 附件/图片 content part:本里程碑明确新增并生成绑定;上传 DTO 只作为输入适配,不作为历史事实源。 +- 不新增图片专用 content part;上传 DTO 只作为输入适配,不作为历史事实源。 - SpacetimeDB schema 或 HTTP API 变更。 ## 依赖与前置条件 @@ -42,7 +42,7 @@ - [x] 未知 part、失效资源或非法路径在持久化前失败关闭。 - [x] canonical item 以 `response_item` 写入历史,标准旧 item 原样可读。 - [x] Codex wire input 不含 AGC 私有 part,且顺序与 canonical content 一致。 -- [x] assistant、附件和工具链路行为无变化;附件与图片按 content 顺序内联,附件-only 输入也能进入 DirectProject prompt。 +- [x] assistant、附件和工具链路行为无变化;所有上传文件按 content 顺序内联,附件-only 输入也能进入 DirectProject prompt。 ## 证据要求 diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index d778bcade..6a444a5c2 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -8769,3 +8769,9 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在 - 决策:采用后台继续运行语义。Direct 回合由进程内项目身份锁持有,页面离开不取消;重进项目通过活动回合只读快照与 Thread Manager bootstrap/consume 恢复忙碌态和进度。左上角面板复用同一快照列出正在运行的 Direct 项目并支持进入。 - 边界:快照不写项目文件、不进入公共 API、不跨应用重启恢复;读取失败保留上一份结果并单独提示,不改写成权限或审批失败。身份锁排他性、付费身份和项目写锁不变。 + +## 2026-09-16 DirectProject 上传文件统一使用附件 content part + +- 决策:DirectProject 本地上传不区分图片与其它文件。用户使用同一个文件入口,前端不按 `mediaType` 做图片判断,所有上传文件统一写成 `agc_attachment_reference`;项目资源 `@` 引用仍使用 `agc_resource_reference`。 +- 原因:原 `agc_image_reference` 与附件 payload 完全相同,Rust wire 投影也把两者合并成同一段文本,不能代表真实的多模态图片输入。保留该 discriminator 只会制造错误语义。 +- 边界:本次不新增 `input_image`,不保留图片类型兼容分支,不迁移旧历史;图片多模态能力未来单独设计独立 payload 与 wire 投影。 diff --git a/docs/【功能说明】AGC聊天素材引用-2026-09-08.md b/docs/【功能说明】AGC聊天素材引用-2026-09-08.md index ca8874954..53f9f46f3 100644 --- a/docs/【功能说明】AGC聊天素材引用-2026-09-08.md +++ b/docs/【功能说明】AGC聊天素材引用-2026-09-08.md @@ -15,6 +15,8 @@ AGC 聊天输入框支持以结构化引用标记当前项目已登记素材。 提交时前端把 Lexical 草稿直接编码为受限 Response API user `message` item:`input_text` 与 AGC 引用 part 按编辑顺序内联在同一个 `content[]` 中。资源引用只携带稳定 `resourceId`;运行画面引用携带区域语义摘要及关联资源 ID。Rust 是唯一 schema source(通过 `ts-rs` 生成 TypeScript 绑定),在发起回合前完成 item 白名单、字段边界、manifest 归属和路径安全校验;校验失败时本轮不持久化、不发送。通过校验的 canonical item 以 `response_item` envelope 写入项目历史,随后由 Rust 将 AGC part 临时转换为 Codex 可接受的 `input_text`,保持原始 content 顺序。已有标准 `response_item` 原样读取与复用;旧 legacy conversation 行不再提供 fallback。 +本地上传入口不区分图片和其它文件:用户从同一个文件选择器提交任意附件,前端不做 MIME 类型分流,所有上传文件统一编码为 `agc_attachment_reference`。图片不会因为扩展名或 MIME 类型获得另一种 canonical part;只有未来真正支持 Codex 多模态 `input_image` 投影时,才另行设计图片协议。 + 当前已完成: - 三个聊天入口共用 `ResourceReferenceInput`;