Update files
This commit is contained in:
@@ -33,13 +33,11 @@ describe("shared messages stream parsing", () => {
|
||||
callId: "call_live",
|
||||
name: "shell",
|
||||
status: "running",
|
||||
input: { command: "ls" },
|
||||
output: null,
|
||||
error: null,
|
||||
detail: {
|
||||
type: "shell",
|
||||
command: "ls",
|
||||
},
|
||||
error: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -7,8 +7,10 @@ function canonicalBase() {
|
||||
type: "tool_call" as const,
|
||||
callId: "call_123",
|
||||
name: "shell",
|
||||
input: { command: "pwd" },
|
||||
output: null,
|
||||
detail: {
|
||||
type: "shell" as const,
|
||||
command: "pwd",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -18,17 +20,12 @@ describe("shared messages tool_call schema", () => {
|
||||
...canonicalBase(),
|
||||
status: "running",
|
||||
error: null,
|
||||
detail: {
|
||||
type: "shell",
|
||||
command: "pwd",
|
||||
},
|
||||
});
|
||||
|
||||
const completed = AgentTimelineItemPayloadSchema.parse({
|
||||
...canonicalBase(),
|
||||
status: "completed",
|
||||
error: null,
|
||||
output: { output: "/tmp/repo" },
|
||||
});
|
||||
|
||||
const failed = AgentTimelineItemPayloadSchema.parse({
|
||||
@@ -54,8 +51,10 @@ describe("shared messages tool_call schema", () => {
|
||||
type: "tool_call",
|
||||
name: "shell",
|
||||
status: "running",
|
||||
input: { command: "pwd" },
|
||||
output: null,
|
||||
detail: {
|
||||
type: "shell",
|
||||
command: "pwd",
|
||||
},
|
||||
error: null,
|
||||
});
|
||||
|
||||
@@ -82,12 +81,11 @@ describe("shared messages tool_call schema", () => {
|
||||
error: null,
|
||||
});
|
||||
|
||||
const missingOutput = AgentTimelineItemPayloadSchema.safeParse({
|
||||
const missingDetail = AgentTimelineItemPayloadSchema.safeParse({
|
||||
type: "tool_call",
|
||||
callId: "call_missing_output",
|
||||
callId: "call_missing_detail",
|
||||
name: "shell",
|
||||
status: "running",
|
||||
input: { command: "pwd" },
|
||||
error: null,
|
||||
});
|
||||
|
||||
@@ -99,7 +97,7 @@ describe("shared messages tool_call schema", () => {
|
||||
|
||||
expect(completedWithError.success).toBe(false);
|
||||
expect(failedWithoutError.success).toBe(false);
|
||||
expect(missingOutput.success).toBe(false);
|
||||
expect(missingDetail.success).toBe(false);
|
||||
expect(legacyStatus.success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -140,14 +140,22 @@ export const AgentPermissionRequestPayloadSchema: z.ZodType<AgentPermissionReque
|
||||
metadata: z.record(z.unknown()).optional(),
|
||||
});
|
||||
|
||||
// Structured tool result types for better client rendering
|
||||
// These types define the structure of the `output` field in tool_call timeline items
|
||||
export type StructuredToolResult =
|
||||
| { type: "command"; command: string; output: string; exitCode?: number; cwd?: string }
|
||||
| { type: "file_write"; filePath: string; oldContent: string; newContent: string }
|
||||
| { type: "file_edit"; filePath: string; diff?: string; oldContent?: string; newContent?: string }
|
||||
| { type: "file_read"; filePath: string; content: string }
|
||||
| { type: "generic"; data: unknown };
|
||||
const UnknownValueSchema = z.union([
|
||||
z.null(),
|
||||
z.boolean(),
|
||||
z.number(),
|
||||
z.string(),
|
||||
z.array(z.unknown()),
|
||||
z.object({}).passthrough(),
|
||||
]);
|
||||
|
||||
const NonNullUnknownSchema = z.union([
|
||||
z.boolean(),
|
||||
z.number(),
|
||||
z.string(),
|
||||
z.array(z.unknown()),
|
||||
z.object({}).passthrough(),
|
||||
]);
|
||||
|
||||
const ToolCallDetailPayloadSchema: z.ZodType<ToolCallDetail> = z.discriminatedUnion("type", [
|
||||
z.object({
|
||||
@@ -180,32 +188,18 @@ const ToolCallDetailPayloadSchema: z.ZodType<ToolCallDetail> = z.discriminatedUn
|
||||
type: z.literal("search"),
|
||||
query: z.string(),
|
||||
}),
|
||||
]);
|
||||
|
||||
const NonUndefinedUnknownSchema = z.union([
|
||||
z.null(),
|
||||
z.boolean(),
|
||||
z.number(),
|
||||
z.string(),
|
||||
z.array(z.unknown()),
|
||||
z.object({}).passthrough(),
|
||||
]);
|
||||
|
||||
const NonNullUnknownSchema = z.union([
|
||||
z.boolean(),
|
||||
z.number(),
|
||||
z.string(),
|
||||
z.array(z.unknown()),
|
||||
z.object({}).passthrough(),
|
||||
z.object({
|
||||
type: z.literal("unknown"),
|
||||
rawInput: UnknownValueSchema,
|
||||
rawOutput: UnknownValueSchema,
|
||||
}),
|
||||
]);
|
||||
|
||||
const ToolCallBasePayloadSchema = z.object({
|
||||
type: z.literal("tool_call"),
|
||||
callId: z.string(),
|
||||
name: z.string(),
|
||||
input: NonUndefinedUnknownSchema,
|
||||
output: NonUndefinedUnknownSchema,
|
||||
detail: ToolCallDetailPayloadSchema.optional(),
|
||||
detail: ToolCallDetailPayloadSchema,
|
||||
metadata: z.record(z.unknown()).optional(),
|
||||
});
|
||||
|
||||
|
||||
@@ -7,8 +7,6 @@ describe("shared tool-call display mapping", () => {
|
||||
const display = buildToolCallDisplayModel({
|
||||
name: "read_file",
|
||||
status: "running",
|
||||
input: { path: "/tmp/repo/src/index.ts" },
|
||||
output: null,
|
||||
error: null,
|
||||
detail: {
|
||||
type: "read",
|
||||
@@ -23,13 +21,16 @@ describe("shared tool-call display mapping", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("does not infer summaries from raw input when detail is missing", () => {
|
||||
it("does not infer summaries from unknown raw detail", () => {
|
||||
const display = buildToolCallDisplayModel({
|
||||
name: "exec_command",
|
||||
status: "running",
|
||||
input: { command: "npm test" },
|
||||
output: null,
|
||||
error: null,
|
||||
detail: {
|
||||
type: "unknown",
|
||||
rawInput: { command: "npm test" },
|
||||
rawOutput: null,
|
||||
},
|
||||
});
|
||||
|
||||
expect(display).toEqual({
|
||||
@@ -37,13 +38,16 @@ describe("shared tool-call display mapping", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps task metadata summary without detail", () => {
|
||||
it("keeps task metadata summary on unknown detail", () => {
|
||||
const display = buildToolCallDisplayModel({
|
||||
name: "task",
|
||||
status: "running",
|
||||
input: null,
|
||||
output: null,
|
||||
error: null,
|
||||
detail: {
|
||||
type: "unknown",
|
||||
rawInput: null,
|
||||
rawOutput: null,
|
||||
},
|
||||
metadata: {
|
||||
subAgentActivity: "Running tests",
|
||||
},
|
||||
@@ -59,9 +63,12 @@ describe("shared tool-call display mapping", () => {
|
||||
const display = buildToolCallDisplayModel({
|
||||
name: "shell",
|
||||
status: "failed",
|
||||
input: null,
|
||||
output: null,
|
||||
error: { message: "boom" },
|
||||
detail: {
|
||||
type: "unknown",
|
||||
rawInput: null,
|
||||
rawOutput: null,
|
||||
},
|
||||
});
|
||||
|
||||
expect(display.errorText).toBe('{\n "message": "boom"\n}');
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { ToolCallTimelineItem } from "../server/agent/agent-sdk-types.js";
|
||||
|
||||
export type ToolCallDisplayInput = Pick<
|
||||
ToolCallTimelineItem,
|
||||
"name" | "status" | "input" | "output" | "error" | "metadata" | "detail"
|
||||
"name" | "status" | "error" | "metadata" | "detail"
|
||||
> & {
|
||||
cwd?: string;
|
||||
};
|
||||
@@ -79,33 +79,35 @@ export function buildToolCallDisplayModel(input: ToolCallDisplayInput): ToolCall
|
||||
let displayName = humanizeToolName(input.name);
|
||||
let summary: string | undefined;
|
||||
|
||||
if (input.detail) {
|
||||
switch (input.detail.type) {
|
||||
case "shell":
|
||||
displayName = "Shell";
|
||||
summary = input.detail.command;
|
||||
break;
|
||||
case "read":
|
||||
displayName = "Read";
|
||||
summary = stripCwdPrefix(input.detail.filePath, input.cwd);
|
||||
break;
|
||||
case "edit":
|
||||
displayName = "Edit";
|
||||
summary = stripCwdPrefix(input.detail.filePath, input.cwd);
|
||||
break;
|
||||
case "write":
|
||||
displayName = "Write";
|
||||
summary = stripCwdPrefix(input.detail.filePath, input.cwd);
|
||||
break;
|
||||
case "search":
|
||||
displayName = "Search";
|
||||
summary = input.detail.query;
|
||||
break;
|
||||
}
|
||||
} else if (lowerName === "task") {
|
||||
switch (input.detail.type) {
|
||||
case "shell":
|
||||
displayName = "Shell";
|
||||
summary = input.detail.command;
|
||||
break;
|
||||
case "read":
|
||||
displayName = "Read";
|
||||
summary = stripCwdPrefix(input.detail.filePath, input.cwd);
|
||||
break;
|
||||
case "edit":
|
||||
displayName = "Edit";
|
||||
summary = stripCwdPrefix(input.detail.filePath, input.cwd);
|
||||
break;
|
||||
case "write":
|
||||
displayName = "Write";
|
||||
summary = stripCwdPrefix(input.detail.filePath, input.cwd);
|
||||
break;
|
||||
case "search":
|
||||
displayName = "Search";
|
||||
summary = input.detail.query;
|
||||
break;
|
||||
case "unknown":
|
||||
break;
|
||||
}
|
||||
|
||||
if (lowerName === "task" && input.detail.type === "unknown") {
|
||||
displayName = "Task";
|
||||
summary = isRecord(input.metadata) ? readString(input.metadata.subAgentActivity) : undefined;
|
||||
} else if (lowerName === "thinking") {
|
||||
} else if (lowerName === "thinking" && input.detail.type === "unknown") {
|
||||
displayName = "Thinking";
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user