Repository navigation
feat(chat): accept bounded UTF-8 document attachments - #7740
huangruiteng merged 2 commits into
Conversation
Signed-off-by: Yuchao Wang <stevenkid404@gmail.com>
Signed-off-by: Yuchao Wang <stevenkid404@gmail.com>
songoow
left a comment
There was a problem hiding this comment.
Reviewer: model_agent · GPT-6 Astra Pro · OpenAI · self_reported
动机 / Motivation
在本机已有 Codex 对话里交付需求和参考材料的用户,需要把小型文档完整交给同一个会话。例如发送一个保留缩进、换行和末尾约束的 requirements.md,旧接口把它当成不支持的图片而拒绝,用户只能复制粘贴;现在可直接附加文件,并在当前对话预览、下载原件,排队后仍交付同一份原文。独立真实 HTTP 对比确认,直送和排队文档由基线400变为当前202,原始 UTF-8 内容进入 native input,重开存储后原件相同,丢失回执重试复用原 Turn。范围是有容量限制的本地 managed Codex 文本附件;不支持 PDF/OCR、任意路径读取、Lark 或其他 Host,也不把协议传输成功当成模型理解或完成了用户任务。完整 GQ06 的材料工作流、其他渠道与已安装 App/真实模型采用仍未完成;本次只交付可独立验收的文档输入及恢复路径。
改动思路 / Approach
扩展现有附件 normalizer、Session/Turn 队列和 Codex adapter,把格式校验与渠道准入留在原来的输入边界;没有材料数据库、第二执行器或新的控制权 owner。当前 PR 保留文档从选择到 native input、持久化与原文回看的完整小切片;不拆开 writer/readback/recovery,也不扩成跨渠道文件平台。这沿用用户原本要完成的工作,不要求额外配置、重新描述已有上下文或再作一次例行确认;需要纠正的输入仍由已有拒绝/重开路径处理。比较不修改、手动复制和另建owner后,当前复用方式对完整性与后续恢复的成本更低。
下面是 exact head ed146ea5fce61854f261f42fcc4bc661e6271cfe 的来源/结果比较,依据实际代码与验证记录整理,不是新的状态机或额外执行证据:
| 路径 | 基线 | 当前 head |
|---|---|---|
| 普通图片 | 一次接受、原件可回读 | 同样行为 |
| 合法文档直送/排队 | 400,未创建工作 | 202、原文输入、同Turn恢复 |
| 不支持渠道/无效文档 | 不接受 | 明确拒绝,草稿可恢复 |
具体改动 / Changes
完整审阅范围:28 files, +405/-50; production attachment transport/UI plus focused tests, browser scenario, bilingual boundary documentation and CI capture retention. 独立规格依据为 https://github.com/loopx-project/loopx/blob/65101d6a6133bbb97c6a7832b20d11e55257ad19/docs/architecture/rfcs/app-conversation-and-async-inbox-v0.md,固定版本 65101d6a6133bbb97c6a7832b20d11e55257ad19。GQ06-bounded-material:implemented; image-admission-replay:implemented; original-source-readback:implemented; GQ06-breadth-live:deferred。这些标识是本次review对既有合同的条目映射;未用作者叙述代替独立契约,也没有把更大路线图改成此PR的隐含完成条件。
关键代码讲解
-
normalize_chat_attachments(loopx/chat_attachments.py:128):严格校验 .md/.txt、UTF-8、NUL、真实字节数和四附件/JSON 容量,图片继续由原 owner 校验;保留 data URL 原文。 -
require_text_document_route(loopx/chat_attachments.py:110):HTTP、runtime、queue 与恢复 adapter 共用同一 local managed Codex 约束;不可把能选文件误作渠道授权。 -
native_attachment_inputs(loopx/chat_attachments.py:177):把文件名标签与完整正文分成 native text 项,明确为用户材料、无额外权限;图片仍为 image 项。 -
DocumentAttachment(apps/presentation/dashboard/src/features/personal-workspace/document-attachment.tsx:6):默认文件卡片可展开 inert 文本并下载原 data URL;composer、普通消息及原对话入口共用该组件。
对主干的风险 / Risks
未发现阻断性代码问题。64,000字节是正文、metadata与文本 base64 的共享 envelope,不能宣传为64KB原文件;图片仍沿原有更大独立额度。显示与 native 传输是已验证边界,模型理解、任意文件和跨渠道使用不在结论内。最需要反证的场景是“界面看起来成功,实际完整原件/原Turn却丢失或来源不再一致”;因此审阅沿 Composer picker/drop -> HTTP envelope + normalize_local_chat_attachments -> existing admission/queue -> Codex start_attachment_turn -> native_attachment_inputs -> original Session store/readers 追到真实持久化或完整读取后的消费者,没有用helper单测替代这条路径。
独立运行三个 Python 文件 71 passed;另用同一真实 HTTP fixture 对比 immutable base 与 head:图片在两侧均只执行一次并回读,文档直送/排队从400且未创建工作变为202并保存完整原文。当前 head 验证 native input 包含材料标签与逐字原文、lost-ACK复用原Turn、重开原件相同及修改内容重试被拒。把新oracle直接用于基线,确实在unsupported-image响应处失败。浏览器证据来自当前CI的document scenario(3096ms完成),我实际查看六张宽/窄/展开/恢复截图,并核验其构建输入。
图片正常额度、正文/metadata/text-base64共享64,000字节的限制、无效UTF-8/NUL、类型/大小不一致、四附件上限及不支持的渠道,都在已有真实输入路径验证。native协议使用合成Codex可执行fixture;不涉及付费模型。
语义与 CI 对齐
复用既有typed输入、历史和权限语义,没有新增业务状态或授予执行权。当前快照的三组 chat-bundle-browser 为成功;本次实际读了 job123998737822 的 document scenario 完成日志。其他 Python 检查仍有未完成状态,未等同于远端全绿。本机离线依赖不足属于验证环境限制,当前缺失的本地browser运行由具备源码/asset绑定的CI执行及实际视觉检查有界补足,不表示本机曾构建成功。没有宣称修复无关红灯,也没有修改预算或删除断言。
我的整体评价 / Verdict
对 exact head ed146ea5fce61854f261f42fcc4bc661e6271cfe 给出 APPROVE。已实际检查六张 source-matched CI packaged 页面:完整桌面、390px窄屏,折叠文件卡片、长原文展开、已接受与拒绝后保留草稿均可辨认;选定这些状态是因为本次新增可展开内容及响应式附件容器。未在本机重建或重新运行浏览器,也未验证已安装 App、真实用户材料或模型采用;实际截图与 CI bundle 的构建来源和当前 head 的671项源码哈希已核对。 按AGENTS的First-Screen Review Evidence,建议把这些已取得的截图直接嵌入PR正文并写明当前revision和packaged边界,方便后续review;这是提交材料建议,本次已实际取得视觉证据,不据截图存放位置虚构功能阻断。 视觉证据:当前CI附件六图。普通输入/完整读取与故障恢复已通过可区分旧缺陷的真实路径验证;维持当前小切片和现有owner,比继续扩大范围更合适。代码认可与远端CI、冲突解决及最终合并资格分开,未授权或执行合并、安装与发布。
English verdict: APPROVE — ed146ea5fce61854f261f42fcc4bc661e6271cfe; the bounded existing conversation journey is covered by independent real-boundary checks, source-matched packaged evidence and actual visual inspection. Local browser-build limitations and remaining merge checks are explicitly separate; no installed or live-model adoption is claimed.
Goal And Delivered Outcome
A user working in an existing local managed Codex conversation can attach a small requirements Markdown file or plain-text source without copying it into the composer. Before this change, the real HTTP endpoint rejected the document as an unsupported image. Now the complete original reaches the native input and remains available for preview and byte-exact download in the same conversation, including after queue recovery.
main, implemented from65101d6a6133bbb97c6a7832b20d11e55257ad19.Author Declaration
Implemented against
Specification: the bounded feature request described here, using the existing Session/Turn attachment and queue contracts at the base revision above. The bilingual App RFC now records the delivered boundary.
.md/.txt, complete original, no truncationloopx/chat_attachments.pytests/test_chat_image_http.pytests/test_chat_queue_preparation.pydocument-attachment.tsx, workspace composer and readersconversation-document-requestbrowser scenarioSelf-check: read repository/contribution/frontend guidance and relevant roadmap/RFCs; inspected current overlapping PR implementation scopes. This adds a material input path, not a separate materials database or control-plane owner.
Scope And Continuation
Up to four combined attachments; text base64, message and metadata share the existing 64,000-byte serialized envelope. Images retain their existing allowance. Strict filename/type/size/UTF-8 checks run before admission. Source is labelled user material and rendered as inert text; it grants no new execution permission. Accepted bytes,
client_turn_id, queue and readback reuse existing owners.Complete within this scope. The broader material-adoption and long-horizon outcome acceptance remain open; no new tracking issue is needed for this bounded request. #7692 addresses delegation-original completeness, #7568 host transport, #7430 controller progression, and #7730 workspace layout; none supplies this file input path. Existing reader/transport ownership remains intact.
Validation
ed146ea5fce61854f261f42fcc4bc661e6271cfe. Backend/integration/static evidence below was collected on the initial feature tree53a70057e447382359aca11032dc462312e9d775; the follow-up changes only attachment control copy and the existing browser selectors. Chat rebuild/TypeScript and frontend contract checks passed again, and the previously failing packagedchat-recoveryscenario now passes locally. The full local browser shard rerun became unobservable when the designated cloud executor disconnected; its final outcome is unknown (no pass claimed).test_chat_image_attachments.py,test_chat_image_http.py,test_chat_queue_preparation.py; real HTTP + filesystem Session + executable synthetic Codex protocol fixture. Includes Chinese, BOM, CRLF, indentation, double spaces, final constraint, busy queue, replay identity/conflict and restart.65101d6a…returned 400 (image attachment must be a supported base64 data URL) with no admitted message; new path completes with exact native text and original readback.build:chat, CSS custom properties, frontend contract, 14 steering recovery tests; configured mypy (17 files), attachment-owner mypy, changed Python Ruff, bilingual RFC and diff checks. Coverage-enabled document browser scenario and 2 dashboard coverage merger tests passed.loopx canary premerge --from-git-diff --git-diff-base 65101d6a6133bbb97c6a7832b20d11e55257ad19 --format json: 6 direct checks passed, 14/17 selected checks passed. Semantic-vocabulary, CLI-output-budget and todo-readmodel checks each returned 0 but fixture cleanup reportedmanaged runtime still exiting. Clean-base comparison is recorded below. Maintainability and public-boundary checks passed.turnsannotations/calls and two optional provider-routing module stubs. Configured mypy passes; full-tree typing is not claimed.Clean-base aggregate comparison: the same 17 selected checks were run on unchanged
65101d6a6133bbb97c6a7832b20d11e55257ad19in an isolated checkout, using that checkout onPYTHONPATH, the same interpreter/dependencies, isolated fixture temporary directories and both CLI comparison refs pinned to that base. It reproduces the same three cleanup failures with child return code 0; 14/17 pass. This is a reproduced baseline/environment limitation, not an all-green aggregate result.Frontend / Visual Evidence
text-document-evidence-*artifact (14-day retention). Current-head packaged visual evidence is available in text-document-evidence-2, bound toed146ea5fce61854f261f42fcc4bc661e6271cfeand retained until November 13, 2026. Reviewer songoow reports inspecting all six source-matched desktop/narrow, expanded-source and rejected-draft screenshots in review 5714851082. These are synthetic packaged-browser fixtures, not installed-App or live-model validation. Direct inline image URLs are not available in the current evidence; inline embedding remains pending.Type of Change
LoopX Area
Technical Direction
Material entry and original-conversation continuity; no model-effectiveness or cost-improvement claim.
Shared-authority RFC fixture impact
N/A: no authority migration, routing promotion, or shared-authority fixture claim.
Boundary Checklist
Signed-off-bytrailer present.