Files
paseo/packages/server/src/server/agent/providers/claude/tool-call-mapper.test.ts
T

295 lines
7.9 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
mapClaudeCompletedToolCall,
mapClaudeFailedToolCall,
mapClaudeRunningToolCall,
} from "./tool-call-mapper.js";
function expectMapped<T>(item: T | null): T {
expect(item).toBeTruthy();
if (!item) {
throw new Error("Expected mapped tool call");
}
return item;
}
describe("claude tool-call mapper", () => {
it("maps running shell calls with canonical fields", () => {
const item = expectMapped(
mapClaudeRunningToolCall({
callId: "claude-call-1",
name: "Bash",
input: { command: "pwd", cwd: "/tmp/repo" },
output: null,
})
);
expect(item.type).toBe("tool_call");
expect(item.status).toBe("running");
expect(item.error).toBeNull();
expect(item.callId).toBe("claude-call-1");
expect(item.detail?.type).toBe("shell");
if (item.detail?.type === "shell") {
expect(item.detail.command).toBe("pwd");
expect(item.detail.cwd).toBe("/tmp/repo");
}
});
it("maps running known tool variants with detail for early summaries", () => {
const readItem = expectMapped(
mapClaudeRunningToolCall({
callId: "claude-running-read",
name: "read_file",
input: { file_path: "README.md" },
output: null,
})
);
expect(readItem.detail).toEqual({
type: "read",
filePath: "README.md",
});
const writeItem = expectMapped(
mapClaudeRunningToolCall({
callId: "claude-running-write",
name: "write_file",
input: { file_path: "src/new.ts" },
output: null,
})
);
expect(writeItem.detail).toEqual({
type: "write",
filePath: "src/new.ts",
});
const editItem = expectMapped(
mapClaudeRunningToolCall({
callId: "claude-running-edit",
name: "apply_patch",
input: { file_path: "src/index.ts" },
output: null,
})
);
expect(editItem.detail).toEqual({
type: "edit",
filePath: "src/index.ts",
});
const searchItem = expectMapped(
mapClaudeRunningToolCall({
callId: "claude-running-search",
name: "web_search",
input: { query: "tool call mapping" },
output: null,
})
);
expect(searchItem.detail).toEqual({
type: "search",
query: "tool call mapping",
});
});
it("maps completed read calls with detail enrichment", () => {
const item = expectMapped(
mapClaudeCompletedToolCall({
callId: "claude-call-2",
name: "read_file",
input: { file_path: "README.md" },
output: { content: "hello" },
})
);
expect(item.status).toBe("completed");
expect(item.error).toBeNull();
expect(item.callId).toBe("claude-call-2");
expect(item.detail?.type).toBe("read");
if (item.detail?.type === "read") {
expect(item.detail.filePath).toBe("README.md");
expect(item.detail.content).toBe("hello");
}
});
it("preserves read content from array/object output variants", () => {
const arrayContent = expectMapped(
mapClaudeCompletedToolCall({
callId: "claude-read-array",
name: "read_file",
input: { file_path: "README.md" },
output: {
content: [
{ type: "output_text", text: "alpha" },
{ type: "output_text", content: "beta" },
],
},
})
);
expect(arrayContent.detail?.type).toBe("read");
if (arrayContent.detail?.type === "read") {
expect(arrayContent.detail.content).toBe("alpha\nbeta");
}
const objectContent = expectMapped(
mapClaudeCompletedToolCall({
callId: "claude-read-object",
name: "read_file",
input: { file_path: "README.md" },
output: {
structured_content: {
content: { type: "output_text", text: "gamma" },
},
},
})
);
expect(objectContent.detail?.type).toBe("read");
if (objectContent.detail?.type === "read") {
expect(objectContent.detail.content).toBe("gamma");
}
});
it("maps failed calls with required error", () => {
const item = expectMapped(
mapClaudeFailedToolCall({
callId: "claude-call-3",
name: "shell",
input: { command: "false" },
output: null,
error: { message: "Command failed" },
})
);
expect(item.status).toBe("failed");
expect(item.error).toEqual({ message: "Command failed" });
expect(item.callId).toBe("claude-call-3");
});
it("maps write/edit/search known shapes with distinct detail types", () => {
const writeItem = expectMapped(
mapClaudeCompletedToolCall({
callId: "claude-write-1",
name: "write_file",
input: { file_path: "src/new.ts", content: "export const x = 1;" },
output: null,
})
);
expect(writeItem.detail?.type).toBe("write");
if (writeItem.detail?.type === "write") {
expect(writeItem.detail.filePath).toBe("src/new.ts");
}
const editItem = expectMapped(
mapClaudeCompletedToolCall({
callId: "claude-edit-1",
name: "apply_patch",
input: { file_path: "src/index.ts", patch: "@@\\n-old\\n+new\\n" },
output: null,
})
);
expect(editItem.detail?.type).toBe("edit");
if (editItem.detail?.type === "edit") {
expect(editItem.detail.filePath).toBe("src/index.ts");
expect(editItem.detail.unifiedDiff).toContain("@@");
}
const searchItem = expectMapped(
mapClaudeCompletedToolCall({
callId: "claude-search-1",
name: "web_search",
input: { query: "tool call mapping" },
output: null,
})
);
expect(searchItem.detail).toEqual({
type: "search",
query: "tool call mapping",
});
});
it("maps unknown tools to unknown detail with raw payloads", () => {
const item = expectMapped(
mapClaudeCompletedToolCall({
callId: "claude-call-4",
name: "my_custom_tool",
input: { foo: "bar" },
output: { ok: true },
})
);
expect(item.status).toBe("completed");
expect(item.error).toBeNull();
expect(item.detail).toEqual({
type: "unknown",
input: { foo: "bar" },
output: { ok: true },
});
});
it("maps Glob calls as search detail using pattern input", () => {
const item = expectMapped(
mapClaudeCompletedToolCall({
callId: "claude-glob-1",
name: "Glob",
input: { pattern: "**/.claude/commands/paseo*" },
output: { output: "No files found" },
})
);
expect(item.status).toBe("completed");
expect(item.error).toBeNull();
expect(item.name).toBe("Glob");
expect(item.detail).toEqual({
type: "search",
query: "**/.claude/commands/paseo*",
});
});
it("normalizes claude speak tool names through schema transforms", () => {
const item = expectMapped(
mapClaudeCompletedToolCall({
callId: "claude-speak-1",
name: "mcp__paseo__speak",
input: { text: "Voice response from Claude." },
output: { ok: true },
})
);
expect(item.name).toBe("speak");
expect(item.detail).toEqual({
type: "unknown",
input: "Voice response from Claude.",
output: null,
});
});
it("normalizes namespaced voice MCP speak tools", () => {
const item = expectMapped(
mapClaudeCompletedToolCall({
callId: "claude-speak-2",
name: "mcp__paseo_voice__speak",
input: { text: "Hey! I can hear you." },
output: { ok: true },
})
);
expect(item.name).toBe("speak");
expect(item.detail).toEqual({
type: "unknown",
input: "Hey! I can hear you.",
output: null,
});
});
it("drops tool calls when callId is missing", () => {
const item = mapClaudeCompletedToolCall({
callId: null,
name: "read_file",
input: { file_path: "README.md" },
output: { content: "hello" },
});
expect(item).toBeNull();
});
});