Merge branch 'investigate/ui-network-responsive' into main

This commit is contained in:
Mohamed Boudra
2026-02-09 12:25:57 +07:00
29 changed files with 442 additions and 297 deletions
@@ -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);
});
});
+22 -33
View File
@@ -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(),
});
@@ -280,11 +274,6 @@ export const AgentStreamEventPayloadSchema = z.discriminatedUnion("type", [
sessionId: z.string(),
provider: AgentProviderSchema,
}),
z.object({
type: z.literal("provider_event"),
provider: AgentProviderSchema,
raw: z.unknown(),
}),
z.object({
type: z.literal("turn_started"),
provider: AgentProviderSchema,
@@ -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}');
+28 -26
View File
@@ -3,7 +3,7 @@ import { stripCwdPrefix } from "./path-utils.js";
export type ToolCallDisplayInput = Pick<
ToolCallTimelineItem,
"name" | "status" | "input" | "output" | "error" | "metadata" | "detail"
"name" | "status" | "error" | "metadata" | "detail"
> & {
cwd?: string;
};
@@ -62,33 +62,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";
}