fix: neutralize codex apply_patch tool description to avoid content-policy block#219
fix: neutralize codex apply_patch tool description to avoid content-policy block#219forrinzhao wants to merge 4 commits into
Conversation
…olicy block
The codex `apply_patch` tool description ("FREEFORM tool, so do not wrap the patch in JSON.") trips Devin Connect content filter when injected via the tool preamble into the system prompt, causing "blocked by our content policy" on Feishu/Lark codex bridge.
sanitizeToolDescriptions() rephrases the two flagged fragments:
FREEFORM -> free-form
"do not wrap the patch in JSON." -> "provide the patch as plain text."
Off-switch: WINDSURFAPI_NEUTRALIZE_TOOL_DESC=0
…olicy block
The codex `apply_patch` tool description ("FREEFORM tool, so do not wrap the patch in JSON.") trips Devin Connect content filter when injected via the tool preamble into the system prompt, causing "blocked by our content policy" on Feishu/Lark codex bridge.
sanitizeToolDescriptions() rephrases the two flagged fragments:
FREEFORM -> free-form
"do not wrap the patch in JSON." -> "provide the patch as plain text."
Off-switch: WINDSURFAPI_NEUTRALIZE_TOOL_DESC=0
…olicy block
The codex `apply_patch` tool description ("FREEFORM tool, so do not wrap the patch in JSON.") trips Devin Connect content filter when injected via the tool preamble into the system prompt, causing "blocked by our content policy" on Feishu/Lark codex bridge.
sanitizeToolDescriptions() rephrases the two flagged fragments:
FREEFORM -> free-form
"do not wrap the patch in JSON." -> "provide the patch as plain text."
Off-switch: WINDSURFAPI_NEUTRALIZE_TOOL_DESC=0
|
哦,Bro,我明白了,但我希望你可以进行过多测试,进行一个完美的黄金PR 我会感谢你合并这个PR |
|
哎我操 我不在日本国 我无法review这个PR我是糟糕困难的抱歉 等待我20天 |
|
Thanks @forrinzhao 🙏 The diagnosis here is genuinely good. Live-bisecting I'm not merging it as-is though. One hunk has to come out, and the main fix should move somewhere else — details and the exact steps below. 1. Drop the
|
|
Correction to my review above — I got one fact wrong, and it flips the severity, so flagging it before you act on it. I wrote " What that actually changes:
So I had the blast radius backwards: the regression doesn't hit some edge config, it hits the source-install default. That's more reason to drop the hunk, not less. Everything else in the review stands. |
…n native path — content-policy (a7) - src/handlers/chat.js: move the DEVIN_CONNECT client-identity neutralize pass to AFTER tool-description preamble injection (was before) so the native-path preamble is covered — root cause of the codex content-policy block - src/handlers/identity-neutralize.js: add rule (a7) — codex apply_patch description 'FREEFORM … do not wrap the patch in JSON.' trips Devin's content filter; rewrite both fragments, tool name + schema untouched (live-bisected 7/7, #219) - test/client-identity-neutralize.test.js: (a7) rewrite / idempotent / no-collateral / off-switch coverage Co-authored-by: forrinzhao <fulin.zhao@gmail.com>
…odex #219 - src/handlers/responses.js: accept bare {role, content} input items (no explicit type:'message') as messages — Codex sends them that way; previously dropped → empty upstream messages → UPSTREAM_INTERNAL - test/responses.test.js: bare-item passthrough coverage Co-authored-by: forrinzhao <fulin.zhao@gmail.com>
- devin-connect.js: DEVIN_CONNECT_BILLING_TAGS 默认 cache_read_tokens=5(已付费校准),缓存命中不再被当作新鲜输入计费;显式 map 覆盖默认,'off' 全关。免费账号零值不编码,无影响 - .env.example: 同步默认值说明 - contributors.json + README 中英: 致谢 forrinzhao(#219 FREEFORM 触发词 + responses 裸 input item) - test: parseBillingTagMap 契约更新(默认值/off/显式覆盖)
|
已合入 master( 采纳的① 你的发现是对的,而且 7/7 确定性复现 + 双片段 A/B(只改一处仍拦、两处都改才过)这个方法尤其对 —— Devin 的 content policy 是非确定性的(同 prompt 先拦后过),重复验证是唯一靠得住的定位手段,项目里 a6 规则当初就是因为拿不到可靠 A/B 才 ship 成默认关的。 但排查你这个 case 的时候,发现根因比两个字符串更深一层: 所以最终版做了两件事:把中和移到 preamble 注入之后,并把你那两条改写做成 ② Codex 确实这么发, 没采纳的
现在的状态
已加入致谢名单(dashboard credits 面板 + README 中英),评级 B+ —— commit 里带了 下次有这类发现,直接开 issue 带最小复现也行,通常比 PR 更快落地。谢谢 🙏 |
合入 warelik 六连(#224-#229):429 reset window 双链根因(resetMs 传输层透传 + 冷却落到 account-wide,原先 model-scoped 对 modelKey=null 的池选择结构性不可见)、 客户端断连不再罚账号(abort 统一识别,流式/非流式全路径不 failover 不烧配额,499)、 Grok/xAI self-ID 中和(a6-grok)、thinking signature 空串改省略、Responses usage 补 input/output_tokens_details、pair-chain 会话连续性新模块(600 行零依赖,默认关)。 合入 forrinzhao #219 的 codex content-policy 发现:落地为规则 (a7),并修根因 —— 工具描述 preamble 原本注入在 neutralizeClientIdentity 之后,导致 native 路径上 任何经由工具描述进来的触发词都绕过 a1-a6 整条防线。identity-neutralize 现覆盖 Claude Code / Cline / Grok / codex 四个客户端。 DEVIN_CONNECT cache_read_tokens=5 付费校准并改为默认解码(#220):缓存命中不再被 当作新鲜输入计费,与 #229 合起来端到端可见;'off' 可全关。setup.sh 在无 LS 二进制 的主机(macOS 典型)默认写 DEVIN_CONNECT=1 + 回环绑定。 2778/0 绿。
Problem
When using the Feishu/Lark codex chatbot bridged through the WindsurfAPI proxy, every request failed with:
Root cause
The codex
apply_patchtool description contains the sentence:When this tool description is injected into the system prompt via the tool preamble, Devin Connect's content filter flags it and rejects the whole request. This is a false positive triggered purely by the wording of the tool description, not by any user content.
Fix
Added
sanitizeToolDescriptions()insrc/handlers/identity-neutralize.js, which rewrites the two flagged fragments:FREEFORM→free-formdo not wrap the patch in JSON.→provide the patch as plain text.It is wired into
src/handlers/chat.jsatconnectTools = sanitizeToolDescriptions(_trim.tools).Can be disabled with env var
WINDSURFAPI_NEUTRALIZE_TOOL_DESC=0.Also included (codex bot support)
src/handlers/responses.js: treat bare{role, content}input items (no explicittype:"message", as Codex sends them) as messages so they aren't dropped; route the Responses path through__route: 'messages'(was'responses').Changes
src/handlers/identity-neutralize.js— new exportedsanitizeToolDescriptions()src/handlers/chat.js— apply sanitization to connect toolssrc/handlers/responses.js— codex input-item + routing handlingwindsurfapi-content-policy.patch— standalone clean patch (applies to upstreammaster)