fix(claude): decloak tool names when toolNameMap misses (#4342)
- Add stripCloakSuffix fallback in decloakToolNames and decloakStreamChunk - Prevent client errors when toolNameMap is missing or lost on retry
This commit is contained in:
@@ -104,14 +104,29 @@ export function cloakClaudeTools(body) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Strip a trailing CLAUDE_TOOL_SUFFIX from a cloaked name as a last-resort
|
||||||
|
// fallback when the name isn't in toolNameMap (e.g. map lost across a retry/
|
||||||
|
// reconnect). Never strips decoy names — those are meant to reach the client
|
||||||
|
// unresolved so it can see "tool unavailable" instead of silently no-oping.
|
||||||
|
function stripCloakSuffix(name) {
|
||||||
|
if (typeof name !== "string" || !name.endsWith(CLAUDE_TOOL_SUFFIX)) return null;
|
||||||
|
if (CC_DEFAULT_TOOLS.has(name)) return null;
|
||||||
|
const original = name.slice(0, -CLAUDE_TOOL_SUFFIX.length);
|
||||||
|
return original.length > 0 ? original : null;
|
||||||
|
}
|
||||||
|
|
||||||
// Decloak tool_use names in non-streaming Claude response body (INPUT side)
|
// Decloak tool_use names in non-streaming Claude response body (INPUT side)
|
||||||
export function decloakToolNames(body, toolNameMap) {
|
export function decloakToolNames(body, toolNameMap) {
|
||||||
if (!toolNameMap?.size || !Array.isArray(body?.content)) return body;
|
if (!Array.isArray(body?.content)) return body;
|
||||||
const content = body.content.map(block => {
|
const content = body.content.map(block => {
|
||||||
if (block?.type === "tool_use" && toolNameMap.has(block.name)) {
|
if (block?.type !== "tool_use") return block;
|
||||||
|
if (toolNameMap?.has(block.name)) {
|
||||||
return { ...block, name: toolNameMap.get(block.name) };
|
return { ...block, name: toolNameMap.get(block.name) };
|
||||||
}
|
}
|
||||||
return block;
|
// toolNameMap missing/stale for this name — fall back to suffix stripping
|
||||||
|
// rather than forwarding an unresolvable "<tool>_ide" name to the client.
|
||||||
|
const fallback = stripCloakSuffix(block.name);
|
||||||
|
return fallback ? { ...block, name: fallback } : block;
|
||||||
});
|
});
|
||||||
return { ...body, content };
|
return { ...body, content };
|
||||||
}
|
}
|
||||||
@@ -126,19 +141,21 @@ export function decloakToolNames(body, toolNameMap) {
|
|||||||
* name appears exactly once per call — on the content_block_start event of
|
* name appears exactly once per call — on the content_block_start event of
|
||||||
* a tool_use block; argument deltas carry no name.
|
* a tool_use block; argument deltas carry no name.
|
||||||
*
|
*
|
||||||
* Unknown names (e.g. a CC decoy tool the model called anyway) pass through
|
* Falls back to stripping the literal CLAUDE_TOOL_SUFFIX when the name isn't
|
||||||
* unchanged, matching the non-streaming decloak behavior.
|
* in toolNameMap (map lost across a retry/reconnect), matching the
|
||||||
|
* non-streaming decloak behavior. Decoy tool names (real CC tool names) and
|
||||||
|
* anything else pass through unchanged.
|
||||||
*
|
*
|
||||||
* @param {object|null} chunk - Parsed SSE event (may be null on stream flush)
|
* @param {object|null} chunk - Parsed SSE event (may be null on stream flush)
|
||||||
* @param {Map|null} toolNameMap - Suffixed → original name map from cloakClaudeTools()
|
* @param {Map|null} toolNameMap - Suffixed → original name map from cloakClaudeTools()
|
||||||
* @returns {object|null} The chunk, with the tool_use name restored when cloaked
|
* @returns {object|null} The chunk, with the tool_use name restored when cloaked
|
||||||
*/
|
*/
|
||||||
export function decloakStreamChunk(chunk, toolNameMap) {
|
export function decloakStreamChunk(chunk, toolNameMap) {
|
||||||
if (!toolNameMap?.size || !chunk || typeof chunk !== "object") return chunk;
|
if (!chunk || typeof chunk !== "object") return chunk;
|
||||||
if (chunk.type !== "content_block_start") return chunk;
|
if (chunk.type !== "content_block_start") return chunk;
|
||||||
const block = chunk.content_block;
|
const block = chunk.content_block;
|
||||||
if (block?.type !== "tool_use" || typeof block.name !== "string") return chunk;
|
if (block?.type !== "tool_use" || typeof block.name !== "string") return chunk;
|
||||||
const original = toolNameMap.get(block.name);
|
const original = toolNameMap?.get(block.name) || stripCloakSuffix(block.name);
|
||||||
if (!original) return chunk;
|
if (!original) return chunk;
|
||||||
return { ...chunk, content_block: { ...block, name: original } };
|
return { ...chunk, content_block: { ...block, name: original } };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user