- 图片错误结果在 Result 页签保留错误名称与代码 - 空文本块加图片的记录按纯图片标注,不再空行 - sourceBlock 的持久化图片守卫检查 attachmentId 字段 - 移除 ui-chat 对 util/crypto 的过期 tsconfig 引用 - 同步 slots.md 层级图与 2026-08-20 所有权 Note
4.5 KiB
Agent Note: Trajectory 持久化图片附件
Status: implemented
English | 中文
Problem
Trajectory 不展示会话图片。持久化的 { type: 'image', attachment: ImageAttachmentRef } 块在详情面板里渲染成格式化 JSON,纯图片的用户消息在记录表中是一个空行。Trajectory 唯一认识的图片路径是对内联 wire 字段(url、image_url、base64 data)的 imageSrc 嗅探,而生产事件从不携带这些字段:每个生产方都在事件追加前提交持久化的 ImageAttachmentRef。用户无法从执行记录确认模型看到了哪张图(issue #2986),而 Chat 已经能展示同样的附件。
Decision
ui-conversation拥有按会话的持久化图片 URL 缓存。HistoricalImageCache从ui-chat移入packages/client/ui-conversation/src/client/conversation/historical-images.ts,以ctx.uiConversation.imageUrl(sessionId, attachment)提供。Chat 与 Trajectory 通过同一实例解析,因此一个会话附件只产生一次session.attachment读取和一个浏览器 URL,并随 Session binding 释放而撤销。这部分取代了 client Session/Conversation 所有权中记录的ui-chat缓存归属。- 画廊 owner 契约(
MessageImagesOwnerProps、RenderMessageImages)移入ui-conversation客户端契约。ui-chat的conversation.message.imagesSlotMap 行沿用共享 owner 类型;ui-trajectory以同一 owner 类型声明自己的子槽位conversation.trajectory.images;ui-attachment把同一个MessageImages画廊组件注册进两个键,因此加载、重试与灯箱行为在两个视图中完全一致。 TrajectorySourceBlock以attachment?: ImageAttachmentRef取代imageSrc/imageAlt。内联来源嗅探(sourceImage、safeImageSource)与 Trajectory 本地的PanelImage渲染器一并删除:没有生产方向会话日志写入内联图片字节或 URL,这些路径是死代码,且 issue 明确排除上传来源的临时路径。- 内容含图片但没有文本的记录,其记录表行以 locale 持有的
layout.imageOnly计数标注;只含图片的工具结果的摘要也使用同一标签,而不是 JSON 转储。 - 存储与 BFF 均不改动:
session.attachment已按会话日志引用授权(缺失、损坏与未被引用的附件显式失败并进入画廊的重试态),sha256 内容寻址已保证每张图片只存一份。
Alternatives considered
保留 Trajectory 自己的 <img> 渲染并喂给它解析好的 URL。 这会重复 ui-attachment 已拥有的加载占位、重试控件和灯箱,并与基于 slot 的附件所有权相抵触,该决定已拒绝跨插件直接 import 组件。
把 conversation.message.images 的声明上提到共享父级,让两个视图渲染同一个键。 renderSlot 的类型限定在声明入口自己的 children 表内,同级的 conversation.view 入口无法渲染另一个入口的子键;slot registry 也拒绝对同一键的第二次声明。共享 owner 类型的第二个键是受支持的组合方式,且允许主题独立替换任一画廊。
在持久化路径之外保留内联 imageSrc 嗅探。 所有生产方(宿主 prompt admission、read_image、MCP 投影、ACP 入口)都在事件追加前提交持久化引用,嗅探不会命中任何东西;保留它等于保留验收标准明确排除的非持久化渲染路径。
Trajectory 自有的图片缓存。 每个视图一份缓存会对同一会话附件发出重复的 session.attachment RPC 和重复的 blob URL,违背"Chat 与 Trajectory 引用同一会话附件"的要求,且没有任何收益。
Consequences
- 两个视图共用一个画廊实现,图片行为(尺寸、重试、灯箱、文案)不会在 Chat 与 Trajectory 之间漂移,且无论多少个视图展示,一个会话附件只读取一次。
TrajectoryTable需要把必填的renderImagesprop 逐层传入详情组件;ui-trajectory新增对dsh-attachment的仅类型依赖,ui-attachment为新的 SlotMap 行新增对ui-trajectory的仅类型依赖。- keyless 组装快照
apps/web/tests/trajectory-image-display.snapshot.ts直接钉住共享缓存这一事实:详情面板中的图片 URL 与 Chat 画廊对同一 fixture 附件的 URL 字符串相同。