设计解析:从 base64 内联到文件名引用的存储与生命周期)
qwen-code 会话附件引用Session Attachment References设计解析从 base64 内联到文件名引用的存储与生命周期【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code导读本文以 qwen-code 设计文档 docs/design/session-attachment-references.md 为骨架系统讲解 daemon 如何将图片与任意文件字节落盘到会话附件目录、以文件名引用attachment reference替代 base64/字节内联从而避免在 daemon 请求、队列、事件与回放replay数据中重复携带大体积负载的设计。读完本文你将掌握附件引用的数据结构与命名规则、存储位置与所有权模型、8 MiB 单文件上限等边界约束以及session_attachments能力与/session/:id/attachmentsHTTP 路由在 TypeScript SDK 与 web-shell 中的真实调用形态。背景问题内联负载带来的重复膨胀在引入附件引用之前图片 base64 与文件字节会被直接嵌入 daemon 的请求体、消息队列、事件流和会话回放数据中。同一份附件在「上传 → 排队 → 转发给 ACP 子进程 → 会话记录回放」的每一跳都会被复制一份产生以下问题负载重复膨胀同一字节在多份请求/事件中反复出现内存与网络开销随链路长度线性放大回放困难附件需要在 daemon 重启后仍可预览内联字节若只存在于进程内存中重启即丢失无统一出处不同客户端各自携带字节缺少单一可信来源single source of truth。设计文档给出的解法非常直接daemon 将图片和任意文件字节写入 workspace runtime 的附件目录只返回一个基于文件名的引用后续所有链路传递的都是这个轻量引用。核心数据结构基于文件名的附件引用daemon 在存储附件后返回如下引用对象{ type: image | resource; attachmentId: string; mimeType: string; size: number; }各字段含义字段说明typeimage图片或resource任意文件资源attachmentId附件在存储目录中的文件名即引用本身mimeType附件 MIME 类型如image/png、application/pdfsize附件字节大小这一结构在 TypeScript SDK 中有完全对应的类型定义见 packages/sdk-typescript/src/daemon/types.ts#L4676-L4681export type DaemonSessionAttachmentReference Recordstring, unknown { type: image | resource; attachmentId: string; mimeType: string; size: number; };配套的读取结果类型为DaemonSessionAttachmentData{ data: string; mimeType: string }data 为 base64 字符串见 packages/sdk-typescript/src/daemon/types.ts#L4683-L4686。命名规则与去重attachmentId 就是存储文件名没有独立的 ID 体系重名文件采用平台通用约定追加序号name (1).ext、name (2).ext以此类推不存在内存中的附件索引也没有 sidecar 元数据MIME 类型与大小在读取时直接从存储文件推导从而避免了索引与实际文件之间的一致性维护成本。引用在链路中的流转Prompt 与 mid-turn API 将引用对象随队列、事件、会话记录元数据transcript metadata一起传递但只有真正向 ACP 子进程派发dispatch时才由 bridge 解析引用——即「引用轻量传递、字节延迟物化」。TypeScript 会话客户端通过带鉴权的附件路由authenticated attachment route读取同一份引用用于预览与回放渲染文本类资源解析为 ACP text其他文件格式解析为 ACP blob原始字节不会在浏览器中被解码或改写。对应实现中上传与解析的桥接逻辑位于 packages/channels/base/src/DaemonChannelBridge.ts含removeAttachment钩子、session_attachments能力预检与按批次扇出上传、以及不支持该能力时的图片内联降级路径浏览器侧的预览/回放水合逻辑见 packages/web-shell/client/daemon/session/actions.ts 与 packages/web-shell/client/daemon/session/types.ts。存储位置与所有权模型存储路径附件落盘于 workspace runtime 的附件目录~/.qwen/tmp/workspace-hash/attachments/session-id/其中workspace-hash是 workspace 的哈希标识session-id是会话 ID使用自定义 runtime 目录时路径等价替换。所有权与鉴权resolved live-session owner 与客户端授权保护每一次上传upload、读取read与移除remove操作关闭 daemon 或客户端断开连接只关闭文件句柄不会删除文件——附件是持久化资源不随连接生命周期消失永久删除会话时其附件目录一并移除避免孤儿文件残留。生命周期策略刻意从简设计上刻意排除了常见的复杂回收机制无 TTL附件不设过期时间无 sweeper没有后台清扫任务无 retained-media cache不维护媒体缓存无重启重建索引daemon 重启不需要重建任何附件索引。这与「无内存索引、无 sidecar 元数据」的设计一脉相承附件就是普通文件文件名即 ID状态完全可由文件系统自身表达。大小限制与配额单个附件上限 8 MiB8 * 1024 * 1024字节会话没有累计附件大小或数量上限。在源码中单文件上限常量定义为SESSION_ATTACHMENT_MAX_ITEM_BYTES 8 * 1024 * 1024另有SESSION_ATTACHMENT_MAX_NAME_BYTES 255限制文件名长度见 packages/acp-bridge/src/sessionAttachments.ts#L13-L14。同一文件中还定义了受支持的图片 MIME 类型集合image/bmp、image/gif、image/jpeg、image/png、image/webp。能力声明与统一 HTTP 接口能力session_attachments统一能力标识为session_attachments在 serve 能力表中声明为v1起可用见 packages/cli/src/serve/capabilities.ts#L57-L58。HTTP 表面/session/:id/attachments统一 HTTP 路由为/session/:id/attachments不存在session_media、/media或mediaId之类的兼容路径——设计上明确只保留一套接口。TypeScript SDK 在 packages/sdk-typescript/src/daemon/DaemonClient.ts 中提供了四个对应的客户端方法方法HTTP 路由作用uploadSessionAttachment(sessionId, data, name, mimeType, opts?)POST /session/:id/attachments?namename上传字节请求体为原始字节流Content-Type即附件 MIME返回引用对象readSessionAttachment(sessionId, attachmentId, opts?)GET /session/:id/attachments/:attachmentId读取附件内容返回{ data: base64, mimeType }listSessionAttachments(sessionId, opts?)GET /session/:id/attachments按上传顺序列出该会话当前存储的全部附件引用removeSessionAttachment(sessionId, attachmentId, opts?)DELETE /session/:id/attachments/:attachmentId删除附件返回{ removed: boolean }从实现细节可以印证设计中的几个要点引用即文件名上传 URL 以 query 参数name携带原始文件名daemon 据此落盘并返回attachmentId即存储文件名见 DaemonClient.ts#L4080-L4102读取时推导 MIME 与大小读取响应的mimeType直接取自响应头content-type缺失时回退为application/octet-stream见 DaemonClient.ts#L4128-L4163浏览器兼容读取实现将Uint8Array分块每块0x8000字节再btoa编码避免超出引擎参数上限注释明确说明该包同时面向 Node 与浏览器环境mid-turn 预检enqueueMidTurnMessage的文档注释提示调用方应先预检session_attachments能力旧 daemon 会忽略 media 字段并丢弃图片内容见 DaemonClient.ts#L4212-L4229。web-shell 会话层将这四个方法进一步封装为uploadAttachment/readAttachment/listAttachments/removeAttachment含read_attachment、remove_attachment等权限动作并会在附加附件块前预检能力见 packages/web-shell/client/daemon/session/types.ts#L565-L581 与 packages/web-shell/client/daemon/session/actions.ts#L2546-L2625。实现层面的安全加固目录防替换校验虽然设计文档保持简洁但从实现可以看到一层额外的持久化安全措施附件存储目录被包装为DurableAttachmentDirectory通过持有目录句柄并校验dev设备号与inoinode 号来检测目录是否被替换如符号链接攻击或目录被删除重建校验失败时抛出Session attachment parent directory changed.见 packages/acp-bridge/src/sessionAttachments.ts#L23-L71。这层校验与文档「resolved live-session owner 和客户端授权保护每一次上传、读取、移除」的所有权要求互相配合共同构成附件读写的安全边界。小结引用式附件设计的取舍会话附件引用方案的核心取舍可以概括为以文件名替代字节内联让队列、事件与回放数据只携带轻量引用字节只存在于磁盘与最终物化环节文件系统即状态无索引、无 sidecar、无 TTL、无重建生命周期完全由会话删除驱动换来极低的维护复杂度边界清晰单文件 8 MiB、文件名 255 字节上限、统一session_attachments能力与/session/:id/attachments路由且不保留任何旧式/media兼容路径。对需要集成 qwen-code daemon 的客户端而言接入路径非常明确预检session_attachments能力 → 通过POST /session/:id/attachments上传并拿到attachmentId→ 在 prompt/mid-turn 内容块中携带引用 → 需要预览或回放时通过GET路由按 ID 读取、按需DELETE。相关类型定义、客户端方法与桥接实现均可在 packages/sdk-typescript/src/daemon、packages/acp-bridge/src/sessionAttachments.ts 与 packages/channels/base/src/DaemonChannelBridge.ts 中进一步查阅。【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考