Repository navigation
fix(chat): preserve both ends of truncated reply context - #7859
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
loopx-agent
left a comment
There was a problem hiding this comment.
REQUEST_CHANGES — 715de568219b38e742a622ac5c5b26bdbcb0c295
[P2] Keep surrogate pairs intact at both truncation boundaries — truncateContent 的 slice(-tail) 会从有效 surrogate pair 中间开始。输入 "🧭".repeat(3000)(6000 个 UTF-16 code units)时,4000-unit 摘录包含孤立的 \udded。同一输入经过实际 native Lark admission、保存的 Turn context 和原会话 HTTP:不可变基线为 200,当前 head 为 400,错误为 UTF-8 无法编码该 surrogate。源消息、当前用户输入和 grant 未改写;这不是模型输出或 provider 故障。
最小修复:Move both prefix and suffix cuts to complete Unicode code-point boundaries while preserving the cap, truthful omission count and original budgets; cover the real saved-context/HTTP path. 回归必须断言有效 Unicode、4000-unit/12000-encoded-unit 上限以及真实原会话 HTTP 可读;仅用长 ASCII 或短 Unicode 不能检出此问题。位置:helper return。
Reviewer: model_agent — gpt-6.1-sol / OpenAI; runtime_reported; reasoning_effort=max.
动机 / Motivation
用户在原会话引用较长消息时,需要同时保留开头和结尾供后续答复。
原先只保留长引用的开头;提案改为两端保留,但有效的长 emoji 引用会被截成不完整字符,导致原会话读取失败。
两端保留的普通文本测试通过;实际 native Lark 保存后的会话 HTTP 读取从基线 200 变为当前 400,尚未交付可靠的长引用阅读。
本次不要求新增编辑器、权限确认、模型调用或完整父级 roadmap;仅要求既有引用与原会话读取保持有效。
原请求和已存在的 scoped authorization 足够,不新增准备、选择或确认轮次。仍保留必要的原来源与激活边界;无需用户重复输入系统已有信息。
改动思路 / Approach
在既有 TypeScript 引用投影中复用一个局部 helper 合理;最小修复是保证两端 Unicode 边界,无需新增状态、权限或编码框架。
当前 PR 仅改变既有引用的内容裁剪;有效长引用、预算与原会话可读性需要一起交付。
实际路径是 Native Lark message admission/private conversation reply context; existing Goal-topic caller also imports the same projection → Existing TypeScript conversation_reply_context projector; new local truncateContent helper → Existing Codex request and original conversation session HTTP snapshot。这条路径的结果必须能被用户后续读取或获知,helper 成功或一张 receipt 不能独自证明交付。采用当前 repository experience 对真实调用、有用结果和既有 owner 的要求,未继承旧审查结论。
具体改动 / Changes
全部 3 文件 +81/-3 已阅读。关键符号:truncateContent, projectParent, projectThread。Verified source parent/thread identities and original provider message content; current user message and grant unchanged;Capped excerpt becomes frozen native Turn context; no source-message rewrite or grant change;UTF-8 encoding fails at existing HTTP response boundary; no new retry owner。
已先检查既有 integrations/collaboration 文档和此 PR 的有界请求;没有规定“两端截断”的已接受书面规范,因此 spec_basis=no_spec,不虚构 RFC 编号或验收里程碑。阻塞来自有效输入经过现有公共路径后无法读取这一独立反例。
复用与简化:既有 typed owner、源读取和 consumer 足够;保留原始身份、授权与回执,不新增持久注释、另一套 provider policy 或 Python 决策真源。修复应留在上述局部 owner;reviewer 没有提交任何产品修改。
对主干的风险 / Risks
PASS:10 个完整 native context 测试;15 个原有 Python integration case;100 组不可变 base/head 非激活/普通引用 byte parity;typecheck、Ruff、5 direct +10 selected premerge checks,以及三文件 public scan(0 errors/warnings)。FAIL:独立真实长 astral 引用 HTTP 反例;16 个直接边界向量中有 4 个从有效变为无效。
原始 Python fake-provider 运行仍记录为 12 failed/3 passed:fake Codex 没有 echo 所请求的 modelProvider。仅在隔离夹具补上协议 echo 后,15 个原有 case 全过,产品和断言未修改;相同受控夹具下新 Unicode case 仍是 base pass/head fail。该原始红项没有被称为通过,也没有被冒充为匹配基线归因。
Same valid astral parent through native Lark admission, frozen Turn context and original-session HTTP: base200, head400; direct16 boundary vectors include4 newly invalid strings. 预期与观察独立于新 helper 的输出定义;缺失通知和 HTTP 400 均是实际公共路径断言失败。feature-off/非激活行为、完整源不可用、done/superseded 与保留回执分别核对,没有用“字段仍在”代替行为对照。未查询、读取或等待远端 CI。没有 authority-store 改动,未把无关 PostgreSQL、真实账号或安装版运行作为通过事实。
本轮 exact-scope CQR 以未解决 correctness blocker 和失败 required regression 记录为 fail;这与正向检查通过并存,不是 merge readiness。修复后应验证最终准确 head,避免仅重跑上一个反例。
我的整体评价 / Verdict
有价值且有界的方案,但当前结果仍不足以交付。long_horizon 与 user_experience 均为 regression:The new tail starts with a lone low surrogate; the saved native Lark context makes GET of the original conversation return HTTP400 instead of baseline200. 最小修复如首条 finding;不要求扩大到父级 roadmap、另建界面或权限流程。
检查 conversation_reply_context.ts、native Lark frozen context 和原会话 HTTP consumer;改动仅替换引用数据,没有页面、布局、控件或 bundle 变更,Unicode/HTTP 反例用实际读取验证。
未声称真实 Lark 账户、已安装客户端、浏览器视觉或模型采用获得资格。
在既有 TypeScript 引用投影中复用一个局部 helper 合理;最小修复是保证两端 Unicode 边界,无需新增状态、权限或编码框架。 当前 PR 仅改变既有引用的内容裁剪;有效长引用、预算与原会话可读性需要一起交付。 本审查只针对该准确 head;未批准、合并或宣称已安装、真实用户采用与持续完整成本验收。
English verdict: REQUEST_CHANGES — head 715de568219b38e742a622ac5c5b26bdbcb0c295 splits valid Unicode and breaks original-session HTTP readback. Existing positive checks pass; the independently reproduced blocker remains unresolved. No merge qualification.
| continue; | ||
| } | ||
| const head = Math.floor(available / 2), tail = available - head; | ||
| return content.slice(0, head) + marker + content.slice(-tail); |
There was a problem hiding this comment.
[P2] 保证两端截断都落在完整 Unicode code point 边界。有效输入 "🧭".repeat(3000)(6000 个 UTF-16 code units)在 cap=4000 时会让尾部从低 surrogate 开始,生成孤立的 \udded。独立通过实际 native Lark admission、保存的 Turn context 和原会话 HTTP 复现:同一不可变 base 返回 200,当前 head 返回 400(UTF-8 无法编码该 surrogate);原源消息、用户输入和 grant 不变。请在保留两端及现有预算时调整 prefix/suffix 切点,并增加有效长 astral 文本经过保存/HTTP 读取的回归,长 ASCII 或短 Unicode 测试覆盖不到此问题。
A truncated parent quote or thread message kept only its head, so a key constraint at the end of a long message silently disappeared from the model's context. Truncation now keeps both ends within the unchanged 4000/12000 budgets and marks the elision explicitly as "...[middle N chars omitted]...", with N exact. Contract fields (content_truncated, omitted_message_count, complete:false) and the no-authority labels are unchanged; short messages render byte-identically. The Python boundary test keeps its forged/foreign/deleted/callback exclusion assertions; the long-parent case now asserts the tail survives with an explicit elision marker and the same encoded budget. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> Signed-off-by: hhyykk <4408344+hhyykk@users.noreply.github.com>
Signed-off-by: huangruiteng <huangrt01@163.com>
715de56 to
65e7d9b
Compare
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent | gpt-6.1-sol | OpenAI | runtime_reported | reasoning_effort=max
APPROVE — 65e7d9baa452bb65ae4ad09407867a82294f457f。未发现阻塞项。
动机 / Motivation
在既有 Lark 会话中引用长消息的用户,需要后续答复同时看到开头背景和结尾约束。
原实现只留开头;前一版两端摘录又会切断 emoji,导致保存后的原会话 HTTP 读取从 200 变成 400。
当前实现保留两端并按完整 Unicode code point 裁剪,独立的保存与读取验证已恢复为 200。
本次只验收既有引用路径的可靠截断,不验收真实租户投递、模型理解或完整历史保留。
改动思路 / Approach
继续复用既有 TypeScript 引用投影、Python effect-runtime facade 和 native Lark admission,位置与职责合理。truncateContent 只负责局部摘录;引用来源校验、当前输入、权限、会话持久化和 HTTP 返回仍由原 owner 负责。一个 helper 同时服务 parent 和 thread,两个小边界 helper 只修复 surrogate pair,当前边界不需要新编码框架、状态、权限或兼容分支。
不改动会继续丢掉长引用结尾;只对 ASCII 留两端又不能解决已复现的 Unicode 读取故障。现在同时覆盖摘录、预算和真实读取,构成可独立验收的修复。marker 计入原预算,其 omitted 数按 JavaScript UTF-16 units 计算;不把 code point 完整性说成完整 grapheme 保留。原消息和当前请求不改写,引用前后的 context-only、无指令或授权语义保持不变。
具体改动 / Changes
完整范围是 79b7818653bc4105ea27a8946de04a9ba997ff03 → 65e7d9baa452bb65ae4ad09407867a82294f457f:3 文件,+180/-4。另检查了上次 715de568219b38e742a622ac5c5b26bdbcb0c295 的问题到当前修复的变化,没有继承旧结论。目标主干快照为 d4aaaaefbd37a83a30f10681de839b45d631eeb9,它与本分支的 merge base 不相同。
算法没有单独 RFC;本轮映射已被修复跟进采用的前次评审,spec revision 5731169614:unicode-boundaries — implemented,两端切口不产生孤立 surrogate;budget-and-readback — implemented,4000-unit parent、最多 12 条及 12000 encoded-unit thread 预算不扩大,实际原会话 HTTP 可读;context-authority — implemented,源消息、当前输入和原 grant 不变。新的行为是有标记地保留两端,并不承诺保留被省略中间的全部内容。
关键代码讲解
safePrefixEnd/safeSuffixStart在切口落入 surrogate pair 时向内移动,避免从有效输入制造非法字符;短内容直接返回,保留原字节与空白。truncateContent把省略标记计入 cap,重新计算实际 omitted 数。极小预算保留 prefix fallback,并同样保证 code point 边界。projectParent/projectThread复用该 helper;thread 继续在完整 encoded row 上二分预算并保留最近的前置消息。Python facade、private conversation 和 Goal-topic 两个既有 caller 均已检查,没有另建来源或授权 owner。
对主干的风险 / Risks
最强反例是有效 emoji 引用经存储后不能读取,而不是 helper 是否返回字符串。我保留同一独立 probe,在原基线验证 HTTP 200,在旧缺陷 head 验证预期的 HTTP 400,再在当前 head 验证 HTTP 200、零孤立 surrogate、原输入与 grant 不变;使用 synthetic provider 和实际 native Turn/session HTTP owner,没有调用真实模型或租户。初版配对脚本曾因我使用错误 route 字段而失败,两边相同;修正到真实 facade 的 source_conversation_id 后重新执行,未将该脚本错误归因于 PR。
| 验证 | 结果 |
|---|---|
| TypeScript reply-context | PASS,12 项,含 thread、短文、错误来源与预算 |
| native Lark 测试与独立 frozen-context/HTTP probe | PASS,17 项 |
| 七组相同输入经真实 Python facade → TS runtime | PASS;短文、非回复、错误来源完全相同,长文四组为已声明摘录变化 |
| control-plane typecheck、changed Python test Ruff | PASS |
| standard diff-selected premerge | PASS,5 direct、10 selected checks |
| strict change-quality | PASS,精确当前 scope 已记录 |
没有 opt-in/default-off 声明;这是既有引用路径的修复。没有新增 actor、权限或共享状态 vocabulary,既有 typed status 继续复用。真实租户、安装后模型采用、完整跨平台资格仍 UNVERIFIED;没有读取或等待远端 CI。恢复非法源 Unicode、grapheme 分割和完整历史检索均不在本次承诺中。
我的整体评价 / Verdict
APPROVE。长程连续使用的相关判断为 improved:当前修复消除了一个会阻断后续会话读取的确定性故障,同时保留原预算和原 owner。用户体验判断为 improved:结尾约束可见,原会话可读,用户不需要新增配置、重复输入、确认或手工恢复。这个结论限于已验证路径,没有推断模型能遵守全部引用内容或节省长期成本。
视觉检查不适用:本次改动为引用数据裁剪与会话读取,不改变渲染布局;真实租户展示与安装后采用未验证。保留现有 helper 和调用边界已足够简洁,未发现需要当前修复承担的额外重构。Approval 是精确 head 的评审结论,合并策略、旧 review 的有效状态和维护者合并权限另行处理。
English verdict: APPROVE at 65e7d9baa452bb65ae4ad09407867a82294f457f. The reproduced Unicode readback defect is fixed; budgets and existing authority boundaries remain intact.
Goal And Delivered Outcome
loopx-project/loopx:main. No issue closure claimed.Author Declaration
Implemented against
origin/main.loopx/control_plane/collaboration/conversation_reply_context.tstests/control_plane_ts/conversation_reply_context.test.tsloopx/control_plane/collaboration/conversation_reply_context.tstests/control_plane_ts/conversation_reply_context.test.tsloopx/control_plane/collaboration/conversation_reply_context.tstests/control_plane_ts/conversation_reply_context.test.tsloopx/control_plane/collaboration/conversation_reply_context.tstests/test_lark_native_reply_context.pyScope And Continuation
Validation
65e7d9baa452bb65ae4ad09407867a82294f457funitpassedtests/control_plane_ts/conversation_reply_context.test.ts: 10 passed, 0 failed (head/tail excerpt, exact omission marker, unchanged budgets, short-text identity, tiny-budget prefix fallback).integrationpassedtests/test_lark_native_reply_context.py: 15 passed; native Lark reply path keeps the reference ending.staticpassedloopx canary premerge --from-git-diff --git-diff-base origin/main(standard tier): diff hygiene, RFC bilingual check, py_compile, maintainability ratchet, semantic vocabulary drift and 8 risk-profile smokes passed; merge gate passed.mainwithout conflicts and all checks above ran on the rebased head.See validation disclosure guidance.
Frontend / Visual Evidence
Type of Change
LoopX Area
Technical Direction
Shared-authority RFC fixture impact
Boundary Checklist
none.Signed-off-bytrailer (git commit -s).Repair Follow-up
truncateContentnow moves both head and tail cuts inward to complete Unicode code-point boundaries while retaining the 4,000 UTF-16-unit parent cap, the 12,000 encoded-unit thread budget and exact omitted-unit counts.