Skip to content

fix: toWireToolName 取错了别名表来源,且作用方向与 CLI 相反(messages 该改没改、tools 声明不该改却改了) #37

Description

@xelr233

结论

d063b47 引入的 TOOL_NAME_ALIASES 重命名表取错了来源,而且作用方向也反了。对照 command-code@1.54.0 的 dist/cli.mjs(未混淆,可直读):

  1. 线上重命名只有一个,不是四个;
  2. 另外三个来自 resolveToolNameAlias,那是客户端修复模型调用旧工具名的逻辑,还带 defaults 语义 —— 与 wire 无关;
  3. 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

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions