* 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:
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user