Skip to content

feat(adapters): shell→apply_patch server-side normalize (refs #235, stacked on #236)#239

Open
Cmochance wants to merge 2 commits into
worktree-fix+apply-patch-custom-tool-call-wirefrom
worktree-feat+shell-to-apply-patch-normalize
Open

feat(adapters): shell→apply_patch server-side normalize (refs #235, stacked on #236)#239
Cmochance wants to merge 2 commits into
worktree-fix+apply-patch-custom-tool-call-wirefrom
worktree-feat+shell-to-apply-patch-normalize

Conversation

@Cmochance
Copy link
Copy Markdown
Owner

@Cmochance Cmochance commented May 21, 2026

Scope

Stacked on top of #236(apply_patch wire 修复)。本 PR 处理 issue #235 的另一个根因:chat-completions provider 上模型偏好 exec_command shell 写文件,Codex Desktop 收不到 apply_patch SSE → 不渲染 diff UI

设计灵感取自 Antigravity Go agent(~/.local/bin/agy strings dump 实证含 FILE_CHANGE_TYPE_EDIT / file_diff / trajectory_file_diffs 等 token,印证 server 层统一 diff 事件模型),adapter 在 SSE 转换层 normalize file-write shell 意图为 custom_tool_call apply_patch wire。

跟 PR #238 关系

PR #238 是 prompt 工程路径(让模型主动选 apply_patch);本 PR 是 server-side normalize 路径(模型选 shell 也能渲染 diff UI)。两条路径不冲突,本 PR ship 后用户可再评估 PR #238 是否仍要 merge(round 3 真机数据出来后)。

MVP scope(3 happy + 严格 reject)

Add File / 完整 overwrite 3 模式(detector tested):

  • cat <<'EOF' > /path/file\n<body>\nEOF(here-doc,unquoted/single/double 终止符)
  • printf '<content>' > /path/file(\n \t \\ \' \" \r 转义展开)
  • echo '<content>' > /path/file(字面 literal,自动补尾 newline)

🚫 严格 reject(透传原 exec_command 不动):

  • >> append / sed -i in-place / tee / tee -a
  • 多命令组合(; && || |)/ 子 shell \$(...) / \...``
  • bare \$VAR / \${VAR}(变量展开 vs 字面 divergence,IMPORTANT-2)
  • > / 2> / &> / echo -e/-n flags / printf %s 格式串
  • here-doc 终止符不合法 / path 含 shell metachar

Edit / append / sed -i / output normalize → docs/followup-tracker.md #39

主要改动

  • 新增 crates/adapters/src/responses/shell_to_apply_patch.rs:detector + V4A patch generator,21 unit test
  • crates/adapters/src/responses/converter.rs:
    • `PendingToolCall` 加 `is_exec_command` flag,open 阶段 buffer 不立即 emit `output_item.added`
    • close 阶段 run detector,detected → emit custom_tool_call apply_patch SSE 序列(call_id 复用模型原 exec_command call_id);未 detected → 补 emit 完整 function_call SSE(行为跟改前 100% 一致)
    • interrupted exec_command 特判 `status="incomplete"` 防 Codex Desktop 跑半截 shell
    • 8 个 integration test 覆盖 happy / reject / interrupted / call_id 复用 / synthetic args / passthrough
  • `docs/CHANGELOG.md` + `docs/investigation/protocol-conversion-3way-comparison.md` 同步
  • `docs/followup-tracker.md` + `docs/followup/39-*.md` 跟踪 Edit/append/sed-i

pre-push 3-agent review

3 IMPORTANT 真 bug 全修 + 2 BLOCKER doc 类全修 + 5 IMPORTANT doc 类全修。详见 commit message。

Verification

Regression baseline + round 3

issue #235 真机 capture(round 1 / round 2)= 26MB + 17 min Kimi+DeepSeek 数据保留 baseline。本 PR push 后 round 3 真机测试,对比 `exec_command` 转 `apply_patch` 触发率 + UI 渲染验证,届时在本 PR 评论补具体数字。

Refs #235


Open in Devin Review

Cmochance added 2 commits May 21, 2026 18:05
…erwrite 3 模式自动触发 Codex Desktop diff UI

PR #236 修了 wire 层(custom_tool_call SSE 桥接 + 多轮历史回放),issue #235 真机
capture(round 2)显示 Kimi 偏好 `exec_command` 跑 `cat <<EOF > file` / `printf >`
/ `echo >` 一次性写文件,绕过 `apply_patch` 工具 → Codex Desktop 收不到 apply_patch
SSE → 不渲染 diff UI(代码块行号 / +N -M / 文件清单 / 颜色 / 抬头 / 可点击文件名
6 个元素失效)。

设计灵感取自 Antigravity Go agent(`~/.local/bin/agy` strings dump 实证含
`FILE_CHANGE_TYPE_EDIT` / `file_diff` / `trajectory_file_diffs` 等 token,印证
server 层产出统一 diff 事件而非依赖模型工具选择),具体 shell → V4A 规则自创。
adapter 在 SSE 转换层归一化 file-write shell 意图为 apply_patch wire 事件:

MVP scope(3 happy pattern)
- `cat <<'EOF' > /path/file\n<body>\nEOF`(here-doc,unquoted/single/double 终止符)
- `printf '<content>' > /path/file`(支持 \n / \t / \\ / \' / \" / \r 转义展开)
- `echo '<content>' > /path/file`(字面 literal,自动补尾 newline)

严格 reject(透传原 exec_command 不动)
- `>>` append / `sed -i` in-place / `tee` / `tee -a` / 多命令组合(`;` `&&` `||`)
- pipe `|` / 子 shell `$(...)` / `` `...` `` / bare `$VAR` `${VAR}` 变量
- 多 `>` / `2>` / `&>` / `echo -e/-n` flags / `printf %s` 格式串
- here-doc 终止符不合法 / path 含 shell metachar

主要改动
- 新增 `crates/adapters/src/responses/shell_to_apply_patch.rs`:detector + V4A
  patch generator,21 个 unit test 覆盖 3 happy + 多个 reject + corner case。
- 改 `crates/adapters/src/responses/converter.rs`:`PendingToolCall` 加
  `is_exec_command` flag,open 阶段对 exec_command **不立即** emit
  `output_item.added`(buffer 整段 args 到 close 阶段);close 阶段 run detector:
  - 检测到 file-write → emit 完整 custom_tool_call apply_patch SSE 序列
    (added → input.delta → input.done → output_item.done),call_id 复用模型
    原 exec_command call_id 让下一 turn tool_result 回灌正确关联。
  - 未检测到 → 补 emit 完整 function_call SSE 序列(added → arguments.delta
    (整段)→ arguments.done → output_item.done),Codex Desktop 当普通 shell 跑,
    行为跟改前 100% 一致。
  - **interrupted exec_command 路径特判** emit `status="incomplete"`,对称
    apply_patch interrupted 处理,防 Codex Desktop 跑半截 shell(truncated
    here-doc 等 destructive)。
- 新增 `docs/followup/39-shell-to-apply-patch-edit-and-append.md` + 索引行:
  Edit / append (`>>` / `tee -a` / `cat <<EOF >>`) / `sed -i` in-place /
  output normalize 跟踪。
- `docs/CHANGELOG.md` + protocol-conversion 3way 同步。

pre-push 3-agent review 已修
- IMPORTANT-1 (code-reviewer):normalize 后 `pending.args_acc` 同步 synthetic
  `{"input":<V4A>}`,跟 ToolCallCache + assistant_message() 三处一致,防下一
  turn previous_response_id 重建给上游 name/args mismatch 数据。
- IMPORTANT-2 (code-reviewer):`echo "hello $USER" > file` reject — bare `$VAR`
  / `${VAR}` shell 会展开,normalize 写字面跟模型意图 divergence(SECRET 落盘等
  危险场景),强制 reject 让模型 fallback apply_patch。
- IMPORTANT-3 (code-reviewer):interrupted exec_command emit `status="incomplete"`
  对称 apply_patch interrupted 处理(原默认 function_call 关闭路径 emit completed
  会让 Codex Desktop 跑半截 shell)。
- BLOCKER B1/B2 (comment-analyzer):followup pointer 补 tee -a 引用 + #39 ref;
  trade-off 注释删除 hard claim(`<1KB` / `<2s`,无数据支撑),改成 issue #235
  capture 数据对照 + round 3 真机后回填。
- Antigravity 措辞精确化(strings dump 证据 vs design 推断的边界明示)。
- doc orphan comment 修复(extract_apply_patch_input 跟 detect 函数 reorder)。
- 5 个新 unit test 覆盖 3 个 IMPORTANT 修复(args_acc 同步 / bare-$ reject /
  interrupted incomplete)。

540 tests pass。

成功率改善幅度待 push 后真机 regression round 3 数据出来回填 PR 评论。

Refs #235
Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant