Repository navigation
Description
结论
d063b47 引入的 TOOL_NAME_ALIASES 重命名表取错了来源,而且作用方向也反了。对照 command-code@1.54.0 的 dist/cli.mjs(未混淆,可直读):
- 线上重命名只有一个,不是四个;
- 另外三个来自
resolveToolNameAlias,那是客户端修复模型调用旧工具名的逻辑,还带defaults语义 —— 与 wire 无关; - CLI 在
messages里重命名,不在tools声明里;当前的实现正好反过来。
(#36 报的下游症状是同一个 bug 的另一面,这里补上根因。)
证据:CLI 1.54.0 的四处源码
① 线上重命名只有一项
nw="search_tools", rw="tool_search", // 两个常量
function toWireToolName(e){return e===rw?nw:e}② 那张四项表是入站别名,不是出站重命名
ow={bash_output:{to:"shell_output"},
task_output:{to:"shell_output",defaults:{wait:"exit"}},
[rw]:{to:nw},
read_multiple_files:{to:"read_file"}}它的消费者是 resolveToolNameAlias:
function resolveToolNameAlias(e){
const t=ow[e.toolName]; if(void 0===t) return e;
const n={...e.input};
for(const[e,r] of Object.entries(t.defaults??{}))
void 0!==n[e]&&null!==n[e]||"wait"===e&&blockCancelsWait(n)||(n[e]=r);
return {toolName:t.to, input:n,
note:\`Repair note: the tool \`\${e.toolName}\` is now \`\${t.to}\`; this call ran as \${t.to} with your arguments carried over. Call \`\${t.to}\` directly next time.\`};
}调用点在工具执行器里(参数是 {toolName,input,signal,onUpdate,toolCallId,permissionMode}),用途是「模型调了退役名 → 本地按新名字跑,并回一句 Repair note 让模型下次改口」。两条与 wire 无关的铁证:
- 它产出
note—— 一段给模型看的自然语言; - 它带
defaults(task_output会补wait:"exit")—— 补默认参数是执行语义,不是重命名。
③ toWireTools 完全不做重命名
function toWireTools(e){return e.map(e=>({name:e.name,description:e.description,input_schema:e.input_schema}))}④ toWireMessages 里 tool-call 与 tool-result 都用别名,且缓存的是别名后的名字
if("tool_use"===t.type){
if(t.providerExecuted) continue;
const r=toWireToolName(t.name);
n.set(t.id,r); // ← 存别名后的名字
e.push({type:"tool-call",toolCallId:t.id,toolName:r,input:t.input});
continue}
...
"tool_result"===t.type && e.push({type:"tool-result",toolCallId:t.tool_use_id,
toolName:n.get(t.tool_use_id)??"unknown", // ← 查出来的也是别名
output:toWireToolOutput(t.content)})当前实现的三处偏差
proxy.mjs:
| 位置 | 现状 | CLI |
|---|---|---|
681 行 TOOL_NAME_ALIASES |
4 项 | 线上只有 1 项(tool_search→search_tools) |
657 行 params.tools[].name |
应用别名 | 不应用(toWireTools 原样) |
525 / 580 行 toolNameMap[tc.id] |
存原名 | 存别名后的名字 |
633 行 assistant tool-call.toolName |
原名 | 别名 |
646 行 tool-result.toolName |
原名(查 map) | 别名 |
也就是说:该改名的地方(messages)没改,不该改的地方(tools 声明)改了。
影响
下游 OpenAI 客户端声明的是 bash_output,wire 上 params.tools 变成 shell_output,而模型如果调用了它,回流的 tool-call.toolName 又是 shell_output —— 客户端按自己声明的 bash_output 找不到对应工具。这正是 #36 描述的现象。
read_multiple_files → read_file、task_output → shell_output 同理。
建议修法
// 线上唯一的重命名(CLI 的 toWireToolName,rw→nw)
const WIRE_TOOL_ALIASES = { tool_search: 'search_tools' };
function toWireToolName(name) { return WIRE_TOOL_ALIASES[name] || name; }
// ① tools 声明原样下发,不套别名
body.params.tools = (tools || []).map(t => ({
name: t.function?.name || t.name || '',
description: ..., input_schema: ...,
}));
// ② messages 里 tool-call / tool-result 都套别名,
// 并且 map 里存**别名后**的名字(CLI 的 n.set(t.id, r))
toolNameMap[tc.id] = toWireToolName(tc.function?.name || '');
parts.push({ type: 'tool-call', toolCallId: tc.id,
toolName: toWireToolName(tc.function?.name || ''), input: ... });至于 bash_output / task_output / read_multiple_files:那是模型侧的退役名,要不要修复是产品决策(CLI 会连 defaults 一起补),但不该出现在 wire 上。
复验方式
curl -sSL "$(curl -sS https://registry.npmjs.org/command-code/latest \
| grep -o 'https://registry.npmjs.org/command-code/-/command-code-[^"]*\.tgz' | head -1)" \
| tar xzO package/dist/cli.mjs > cli.mjs
grep -o 'function toWireToolName([^}]*}' cli.mjs # → e===rw?nw:e
grep -o 'ow={[^}]*}[^}]*}' cli.mjs # → 四项表
grep -o 'function toWireTools([^}]*}' cli.mjs # → 无别名附:顺带提一句,README 里引用的 PROTOCOL-FACTS-1.53.1.md 在仓库里不存在(git ls-tree -r upstream/master 只有 12 个文件),是悬空引用。
Activity
fix: 工具名重写对齐 CLI —— 只在 messages 里改,且只改一项
xelr233 commentedon Sep 14, 2026
结论:这张别名表应该整体删掉,
toWireToolName改回恒等补充 #37 里没拿到的那块拼图 ——
tool_search是 CLI 自己退役的名字,而且不可见:它确实从不上网:
调用方那边,发请求用的那次
getSchemas不带includeHidden,只有本地查 schema 才开:所以
params.tools里永远没有tool_search。四个函数排在一起就完全自洽了:toWireTools{name,description,input_schema}toWireMessagestool-call与tool-result都用toWireToolName,map 存改后的名字resolveToolNameAlias(ow表)Repair note,还补defaultstoWireToolName不是「工具重命名设施」,而是「把自家 catalog 里那一个退役名字的历史归一化」。 它成立的前提是:CLI 自己退役过工具名,而且可能在重放用旧名字录下来的会话。proxy 没有这个前提
params.tools来自下游客户端,proxy 没有 catalog、没有退役名;tool_search的工具,把它改掉是纯粹的错。而且现在的改法(只改声明、不改消息)连内部自洽都做不到:CLI 是「声明用当前名 + 消息也用当前名」,现在是「声明改名 + 消息不改名」,两头都不对。
建议改法
① 默认(推荐):删掉整张表。
声明与消息天然一致,#36 的症状直接消失,也不需要任何「改回去」的对称逻辑。
② 若确实要支持「重放真实 CLI 旧会话」:那也不该是出站改写,而应是入站归一化 + 显式开关(只有下游本身就是 CC CLI 形态的 harness 时才开),并且连
defaults一起补,与resolveToolNameAlias同语义。这样声明与消息仍然一致。唯独不要做成「上行改、下行不改」。关于 #36
症状描述是对的(声明与消息不一致 → 下游派发不了),但有两处需要更正:
bash_output成立,推广不成立。CLI 确实会改tool_search,只是只改这一项、只在messages里改;visible:()=>false),纯粹是重放历史用的。至于「在原工具名上加几个随机字符(
bash_output-cmdc-m)」这个提议,方向是反的:params.tools名字完全一样(shell_output/read_file/edit_file…),「多账号工具名相同」本来就是常态,加随机后缀反而制造了一个所有真实 CLI 都没有的偏差;toWireTools是原样下发。复验
(我这边已按 ① 改完并在真机 mock 上验证:声明与消息现在都原样透传;新增 4 条 wire 契约断言覆盖「声明不改名 /
tool_search在消息里也不改名 / tool-result 与 tool-call 名字一致」。)fix: 工具名一个都不重命名 —— 删除 toWireToolName(383aed8 的收尾)
jinyu2022 commentedon Sep 15, 2026
我提议的增加随机字符,主要是为了方便回传。最初我只是简单地在回传逻辑中增加了一张反向映射表,但很快发现,如果直接进行新旧值映射,下游传入新值时,也可能被错误地映射回旧值。
因此,还需要额外增加一个字段,用于标识下游传入的是否为新的工具名;如果是新工具名,则不再进行映射。这样一来,整体实现的代码量就会增加不少,不如直接自定义一个随机的下游不可能传的后缀。
当然我也同意直接删去映射,直接请求。
特征方面近乎无解,最好是让下游别用这几个工具名:
MAXeaglet commentedon Oct 9, 2026
已随 PR #58 合入修复:\ oWireToolName\ 单向别名表已被彻底废除,所有工具名在 wire 层面全程原样透传,声明与消息保持同名一致。