--- phase: 06- plan: 01 type: execute wave: 1 depends_on: [] files_modified: - frontend/src/core/messages/utils.ts - frontend/src/components/ai-elements/prompt-input.tsx - frontend/src/core/threads/hooks.ts - frontend/src/core/threads/hooks.test.ts autonomous: true requirements: - ATREF-03 must_haves: truths: - "用户发送带文件引用的消息后,消息体仍通过 additional_kwargs.files 传输,不新增并行主结构。" - "引用文件在提交结构中可区分来源与类型,且不破坏现有文件渲染链路。" - "引用项失效时会被自动剔除并提示,但文本消息仍可发送。" artifacts: - path: "frontend/src/core/messages/utils.ts" provides: "FileInMessage 扩展字段(引用来源/类型)与兼容解析" - path: "frontend/src/components/ai-elements/prompt-input.tsx" provides: "PromptInputMessage 新增引用文件字段契约" - path: "frontend/src/core/threads/hooks.ts" provides: "上传文件与引用文件合并提交到 additional_kwargs.files" - path: "frontend/src/core/threads/hooks.test.ts" provides: "提交结构与软失败行为的单元测试" key_links: - from: "frontend/src/components/ai-elements/prompt-input.tsx" to: "frontend/src/core/threads/hooks.ts" via: "PromptInputMessage.references" pattern: "references" - from: "frontend/src/core/threads/hooks.ts" to: "frontend/src/core/messages/utils.ts" via: "FileInMessage 扩展字段" pattern: "additional_kwargs:\\s*\\{\\s*files" --- 先定义并落地“引用文件提交契约”,确保 Phase 6 的数据链路稳定可回归。 Purpose: 把最难回滚的协议与提交流程先锁定,避免后续 UI 实现完成后才发现协议不兼容。 Output: 扩展后的消息类型、提交流程、以及针对合并/软失败的自动化测试。 @/home/mt/.codex/get-shit-done/workflows/execute-plan.md @/home/mt/.codex/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/06-/06-CONTEXT.md @.planning/phases/06-/06-RESEARCH.md @.planning/phases/06-/06-VALIDATION.md @frontend/src/components/ai-elements/prompt-input.tsx @frontend/src/core/messages/utils.ts @frontend/src/core/threads/hooks.ts @frontend/src/core/threads/hooks.test.ts From `frontend/src/components/ai-elements/prompt-input.tsx`: ```typescript export type PromptInputMessage = { text: string; files?: FileUIPart[]; }; ``` From `frontend/src/core/messages/utils.ts`: ```typescript export interface FileInMessage { filename: string; size: number; path?: string; status?: "uploading" | "uploaded"; } ``` From `frontend/src/core/threads/hooks.ts`: ```typescript const filesForSubmit: FileInMessage[] = uploadedFileInfo.map(...) await thread.submit({ messages: [{ type: "human", additional_kwargs: { files: filesForSubmit } }], }); ``` Task 1: 扩展引用文件契约并写 RED 测试 frontend/src/core/messages/utils.ts, frontend/src/components/ai-elements/prompt-input.tsx, frontend/src/core/threads/hooks.test.ts - frontend/src/core/messages/utils.ts - frontend/src/components/ai-elements/prompt-input.tsx - frontend/src/core/threads/hooks.test.ts - .planning/phases/06-/06-CONTEXT.md - Test 1: `PromptInputMessage` 支持 `references` 字段,类型可表达 `artifact|upload` 来源(per D-06)。 - Test 2: `FileInMessage` 支持可选 `ref_kind/ref_source` 元数据且旧字段保持可用(per D-05, D-06)。 在 `PromptInputMessage` 新增 `references` 数组字段;在 `FileInMessage` 增加 `ref_kind: "mention"` 与 `ref_source: "artifact" | "upload"` 可选字段;先在 `hooks.test.ts` 新增失败用例,断言提交 payload 含 `additional_kwargs.files[*].ref_kind/ref_source` 且不删除已有 `filename/size/path/status` 字段(按 D-05、D-06)。 - `rg -n "references\\?:" frontend/src/components/ai-elements/prompt-input.tsx` 返回至少 1 行。 - `rg -n "ref_kind|ref_source" frontend/src/core/messages/utils.ts` 返回至少 2 行。 - 新增测试在实现前失败(RED),失败信息包含 `ref_kind` 或 `ref_source` 字样。 cd frontend && node --test src/core/threads/hooks.test.ts 类型契约完成并有可复现的失败测试,明确约束提交结构。 Task 2: 在线程提交链路合并上传文件与引用文件并实现软失败 frontend/src/core/threads/hooks.ts, frontend/src/core/threads/hooks.test.ts - frontend/src/core/threads/hooks.ts - frontend/src/core/threads/hooks.test.ts - frontend/src/core/uploads/api.ts - .planning/phases/06-/06-CONTEXT.md - .planning/phases/06-/06-RESEARCH.md - Test 1: 上传文件 + 引用文件会统一写入 `additional_kwargs.files`,且上传文件不被覆盖(per D-05)。 - Test 2: 引用失效时仅剔除失效项并 toast,文本仍会继续提交(per D-07)。 在 `sendMessage` 中新增引用文件合并逻辑:`uploadedFileInfo` 先转 `FileInMessage`,再追加 `message.references`(保留 `ref_kind/ref_source`);提交前根据传入的有效引用列表进行二次过滤,失效项通过 `toast.error("部分引用已失效,已自动移除")` 提示并继续 `thread.submit`;禁止创建 `mentions` 等并行结构(按 D-05、D-07)。 - `rg -n "additional_kwargs:\\s*\\{\\s*files" frontend/src/core/threads/hooks.ts` 命中提交代码。 - `rg -n "ref_kind|ref_source" frontend/src/core/threads/hooks.ts` 命中引用元信息写入。 - `rg -n "已自动移除|stale" frontend/src/core/threads/hooks.ts` 命中软失败分支。 cd frontend && node --test src/core/threads/hooks.test.ts 提交链路兼容 uploads + references,软失败生效且单测通过。 ## Trust Boundaries | Boundary | Description | |----------|-------------| | input-box→thread submit API | 用户可控输入跨越到后端提交 envelope | | thread artifacts/uploads→引用元信息 | 候选文件元数据进入消息体 | ## STRIDE Threat Register | Threat ID | Category | Component | Disposition | Mitigation Plan | |-----------|----------|-----------|-------------|-----------------| | T-06-01-01 | T | `frontend/src/core/threads/hooks.ts` | mitigate | 仅接受候选池中引用并在提交前二次过滤,拒绝自由路径注入(ASVS V5)。 | | T-06-01-02 | I | `additional_kwargs.files` | mitigate | 强制 thread 范围来源,不引入全局检索,避免跨线程信息泄露(ASVS V4)。 | | T-06-01-03 | D | `sendMessage` 合并逻辑 | mitigate | 失效引用软剔除并继续提交,避免单点失败阻断消息发送。 | - `cd frontend && node --test src/core/threads/hooks.test.ts` - `cd frontend && pnpm -s typecheck` - `additional_kwargs.files` 成为上传与引用的唯一提交结构。 - 引用元信息可被编码且不影响既有文件渲染。 - 失效引用不会导致整条消息发送失败。 After completion, create `.planning/phases/06-/06-01-SUMMARY.md`