# 现象(全功能演练抓到,根因来自 opencode 自己的内部记录)
opencode 把四个步骤全做完了(2× download_attachment、read 读到内容、
upload_attachment 成功),却在最后一步卡死:`send_mail` 连续 **6 次**失败,
然后放弃整个任务,自述为「attachment_ids 参数有框架级序列化 bug」。
真实形状(从 opencode 的 part 表里取出的原始输入):
input.attachment_ids = "[\"10e73e9f-c2a9-4226-bdb5-34ef1b340eb8\"]" ← 字符串
error: 字段 "attachment_ids" 类型不对:期望 string 数组,收到 string
模型把数组写成了 **JSON 字符串**,桥原样转发,服务端的严格解码器按契约拒收。
# 修在哪一层
**不在服务端放宽。** 那个「严格」是刻意的,挡的是字段名拼错、结构写错这类真
错误 —— 松开之后真 bug 会被静默接受(同一封邮件少几个附件,HTTP 仍是 200)。
**在桥这一层收。** 桥是适配器:模型侧的形状天生不可靠,而适配器的职责就是把
不可靠的输入归一成契约要求的形状。对模型宽容、对服务端严格 —— 这与
homeagent 那个 Go 插件里的 `stringList` 是同一个判断(那边注释写着「也接受
单个字符串……拒绝它只会换来一次重试,而意图毫无歧义」)。
接受的形状:数组 / JSON 数组字符串 / 单个 id / 逗号或空白分隔 / 混进 null
与数字时丢掉坏的保留好的。空串与 null 一并丢掉,与服务端
`parseAttachmentIDs` 保持一致。
# 三桥同源
新增共用模块 `lib/attachment-ids.js` + 同名测试,已加入
`deploy/check-shared-libs.sh` 的两个清单(实现与测试都必须逐字节相同 ——
只同步实现不同步测试,等于允许一侧偷偷放宽约定)。
三份 md5 一致,检查脚本通过。
# 测试
`test/attachment-ids.test.mjs` 17 条,三桥各一份。含**反向对照**:把 JSON 字符串
分支去掉后必须变红(实测 3 条失败)—— 否则这条判据就是空转,事故会复发。
全量:pi 401 / dsh 361 / opencode 312,0 失败。
43 lines
2.0 KiB
Bash
Executable File
43 lines
2.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# 共用模块必须逐字节相同 —— 见 docs/PLUGIN-CONTRACT.md 第六节。
|
||
#
|
||
# 一侧改了另一侧没改,几个平台的行为就会悄悄分叉:同一封邮件在 opencode 那边
|
||
# 标了已读、在 DSH 那边没标,而两处代码看起来都"对"。
|
||
#
|
||
# 三方比对以 opencode 为基准逐个对比,而不是两两对比:后者在三方都不同时
|
||
# 会打出三条互相矛盾的差异,读的人无从判断谁是对的。
|
||
set -euo pipefail
|
||
BASE=plugins/opencode-mail-bridge
|
||
PEERS=(plugins/dsh-mail-bridge plugins/pi-mail-bridge)
|
||
fail=0
|
||
|
||
for peer in "${PEERS[@]}"; do
|
||
for f in relay-dedup relay-policy relay-key permission-mode bounded inbox-format session-snapshot workspace model-scope catchup addressing discovery rename-proposal permission-grants adopt sse-client user-question attachment-ids; do
|
||
if [[ ! -f "$peer/lib/$f.js" ]]; then
|
||
echo "共用模块缺失:$peer/lib/$f.js" >&2
|
||
fail=1
|
||
continue
|
||
fi
|
||
if ! diff -q "$BASE/lib/$f.js" "$peer/lib/$f.js" >/dev/null 2>&1; then
|
||
echo "共用模块已分叉:lib/$f.js($BASE vs $peer)" >&2
|
||
diff "$BASE/lib/$f.js" "$peer/lib/$f.js" | head -20 >&2
|
||
fail=1
|
||
fi
|
||
done
|
||
# 测试同样要同源:共用模块的行为约定写在测试里,
|
||
# 只同步实现不同步测试,等于允许一侧偷偷放宽约定。
|
||
for f in relay-policy relay-key permission-mode bounded inbox-format session-snapshot workspace model-scope catchup addressing discovery rename-proposal permission-grants adopt sse-client user-question attachment-ids; do
|
||
if [[ ! -f "$peer/test/$f.test.mjs" ]]; then
|
||
echo "共用测试缺失:$peer/test/$f.test.mjs" >&2
|
||
fail=1
|
||
continue
|
||
fi
|
||
if ! diff -q "$BASE/test/$f.test.mjs" "$peer/test/$f.test.mjs" >/dev/null 2>&1; then
|
||
echo "共用测试已分叉:test/$f.test.mjs($BASE vs $peer)" >&2
|
||
fail=1
|
||
fi
|
||
done
|
||
done
|
||
|
||
[[ $fail -eq 0 ]] && echo " 共用模块三方同源(opencode / dsh / pi)" || exit 1
|