fix: route Claude partial tool input through canonical mapping (#108) (#122)

* fix: show loading skeleton for pending tool call details

* fix: surface claude tool previews before full json

* fix: route claude partial tool input through canonical mapping

* fix: remove pending tool-call summary placeholder
This commit is contained in:
Mohamed Boudra
2026-03-17 14:42:39 +08:00
committed by GitHub
parent 9364da3414
commit 11f6494c20
10 changed files with 723 additions and 24 deletions
@@ -0,0 +1,60 @@
import assert from "node:assert/strict";
import { describe, it } from "vitest";
import type { ToolCallDetail } from "@server/server/agent/agent-sdk-types";
import {
hasMeaningfulToolCallDetail,
isPendingToolCallDetail,
} from "./tool-call-detail-state";
describe("tool-call detail state", () => {
it("treats empty unknown payloads as not meaningful", () => {
const detail: ToolCallDetail = {
type: "unknown",
input: {},
output: null,
};
assert.strictEqual(hasMeaningfulToolCallDetail(detail), false);
});
it("treats partial unknown payloads with real values as meaningful", () => {
const detail: ToolCallDetail = {
type: "unknown",
input: { path: "src/index.ts" },
output: null,
};
assert.strictEqual(hasMeaningfulToolCallDetail(detail), true);
});
it("marks running calls with no meaningful detail as pending", () => {
assert.strictEqual(
isPendingToolCallDetail({
detail: {
type: "unknown",
input: {},
output: null,
},
status: "running",
error: null,
}),
true
);
});
it("does not mark completed calls as pending", () => {
assert.strictEqual(
isPendingToolCallDetail({
detail: {
type: "unknown",
input: {},
output: null,
},
status: "completed",
error: null,
}),
false
);
});
});
@@ -0,0 +1,69 @@
import type { ToolCallDetail } from "@server/server/agent/agent-sdk-types";
function hasMeaningfulUnknownValue(value: unknown): boolean {
if (value === null || value === undefined) {
return false;
}
if (typeof value === "string") {
return value.trim().length > 0;
}
if (typeof value === "number" || typeof value === "boolean") {
return true;
}
if (Array.isArray(value)) {
return value.some(hasMeaningfulUnknownValue);
}
if (typeof value === "object") {
return Object.values(value).some(hasMeaningfulUnknownValue);
}
return true;
}
export function hasMeaningfulToolCallDetail(
detail: ToolCallDetail | undefined
): boolean {
if (!detail) {
return false;
}
switch (detail.type) {
case "shell":
return true;
case "read":
return Boolean(detail.filePath || detail.content);
case "edit":
return Boolean(
detail.filePath || detail.unifiedDiff || detail.oldString || detail.newString
);
case "write":
return Boolean(detail.filePath || detail.content);
case "search":
return detail.query.trim().length > 0;
case "worktree_setup":
return Boolean(detail.branchName || detail.worktreePath || detail.log);
case "sub_agent":
return Boolean(
detail.subAgentType ||
detail.description ||
detail.log ||
detail.actions.length > 0
);
case "plain_text":
return Boolean(detail.label || detail.text);
case "unknown":
return (
hasMeaningfulUnknownValue(detail.input) ||
hasMeaningfulUnknownValue(detail.output)
);
}
}
export function isPendingToolCallDetail(params: {
detail: ToolCallDetail | undefined;
status: "executing" | "running" | "completed" | "failed" | "canceled";
error: unknown | null | undefined;
}): boolean {
const isRunning =
params.status === "running" || params.status === "executing";
return isRunning && params.error == null && !hasMeaningfulToolCallDetail(params.detail);
}