diff --git a/packages/app/src/components/tool-call-details.tsx b/packages/app/src/components/tool-call-details.tsx
index f66282dfe..7af089cd4 100644
--- a/packages/app/src/components/tool-call-details.tsx
+++ b/packages/app/src/components/tool-call-details.tsx
@@ -382,15 +382,6 @@ interface SearchDetail {
function buildSearchSections(detail: SearchDetail, ds: DetailStyles): ReactNode[] {
const out: ReactNode[] = [];
- if (detail.query) {
- out.push(
-
-
- {detail.query}
-
- ,
- );
- }
if (detail.content) {
out.push(
diff --git a/packages/server/src/server/agent/providers/claude-agent.redesign.test.ts b/packages/server/src/server/agent/providers/claude-agent.redesign.test.ts
index be01b7b15..432081f51 100644
--- a/packages/server/src/server/agent/providers/claude-agent.redesign.test.ts
+++ b/packages/server/src/server/agent/providers/claude-agent.redesign.test.ts
@@ -620,6 +620,56 @@ test("maps tool_result content shapes into deterministic string output", async (
}
});
+test("Grep tool_result string content flows to a search detail with content", async () => {
+ const session = await createSession();
+ const internal = session as unknown as {
+ buildToolOutput: (
+ block: Record,
+ entry: Record | undefined,
+ ) => Record | undefined;
+ };
+
+ const grepEntry = {
+ id: "tool-grep-1",
+ name: "Grep",
+ server: "Grep",
+ classification: "search",
+ started: true,
+ input: { pattern: "MaskedView", output_mode: "files_with_matches" },
+ };
+
+ try {
+ const output = internal.buildToolOutput(
+ {
+ type: "tool_result",
+ tool_use_id: "tool-grep-1",
+ tool_name: "Grep",
+ content: "Found 2 files\nsrc/foo.tsx\nsrc/bar.tsx",
+ is_error: false,
+ },
+ grepEntry,
+ );
+
+ const { mapClaudeCompletedToolCall } = await import("./claude/tool-call-mapper.js");
+ const item = mapClaudeCompletedToolCall({
+ callId: "tool-grep-1",
+ name: "Grep",
+ input: grepEntry.input,
+ output: output ?? null,
+ });
+
+ expect(item?.detail).toEqual({
+ type: "search",
+ query: "MaskedView",
+ toolName: "grep",
+ content: "Found 2 files\nsrc/foo.tsx\nsrc/bar.tsx",
+ numFiles: 0,
+ });
+ } finally {
+ await session.close();
+ }
+});
+
test("completes a foreground run when only system metadata arrives before the first assistant message", async () => {
let step = 0;
sdkQueryFactory.mockImplementation(() =>
diff --git a/packages/server/src/server/agent/providers/claude/tool-call-detail-parser.ts b/packages/server/src/server/agent/providers/claude/tool-call-detail-parser.ts
index a03deac85..a63e8b715 100644
--- a/packages/server/src/server/agent/providers/claude/tool-call-detail-parser.ts
+++ b/packages/server/src/server/agent/providers/claude/tool-call-detail-parser.ts
@@ -25,6 +25,16 @@ import {
toolDetailBranchByName,
} from "../tool-call-detail-primitives.js";
+const ClaudeGrepOutputSchema = z
+ .union([
+ ToolGrepOutputSchema,
+ z
+ .object({ output: z.string() })
+ .passthrough()
+ .transform(({ output }) => ({ numFiles: 0, filenames: [], content: output })),
+ ])
+ .nullable();
+
const ClaudeToolEnvelopeSchema = z
.object({
name: z.string().min(1),
@@ -128,17 +138,11 @@ const ClaudeToolDetailPass2Schema = z.union([
toolDetailBranchByName("search", ToolSearchInputSchema, z.unknown(), (input) =>
toSearchToolDetail({ input, toolName: "search" }),
),
- toolDetailBranchByName(
- "Grep",
- ToolSearchInputSchema,
- ToolGrepOutputSchema.nullable(),
- (input, output) => toSearchToolDetail({ input, output, toolName: "grep" }),
+ toolDetailBranchByName("Grep", ToolSearchInputSchema, ClaudeGrepOutputSchema, (input, output) =>
+ toSearchToolDetail({ input, output, toolName: "grep" }),
),
- toolDetailBranchByName(
- "grep",
- ToolSearchInputSchema,
- ToolGrepOutputSchema.nullable(),
- (input, output) => toSearchToolDetail({ input, output, toolName: "grep" }),
+ toolDetailBranchByName("grep", ToolSearchInputSchema, ClaudeGrepOutputSchema, (input, output) =>
+ toSearchToolDetail({ input, output, toolName: "grep" }),
),
toolDetailBranchByName(
"Glob",
diff --git a/packages/server/src/server/agent/providers/claude/tool-call-mapper.test.ts b/packages/server/src/server/agent/providers/claude/tool-call-mapper.test.ts
index 1f0dd4824..531405285 100644
--- a/packages/server/src/server/agent/providers/claude/tool-call-mapper.test.ts
+++ b/packages/server/src/server/agent/providers/claude/tool-call-mapper.test.ts
@@ -310,6 +310,25 @@ describe("claude tool-call mapper", () => {
});
});
+ it("maps Grep calls when output arrives as the claude-agent string wrapper", () => {
+ const item = expectMapped(
+ mapClaudeCompletedToolCall({
+ callId: "claude-grep-string-1",
+ name: "Grep",
+ input: { pattern: "MaskedView", output_mode: "files_with_matches" },
+ output: { output: "Found 2 files\nsrc/foo.tsx\nsrc/bar.tsx" },
+ }),
+ );
+
+ expect(item.detail).toEqual({
+ type: "search",
+ query: "MaskedView",
+ toolName: "grep",
+ content: "Found 2 files\nsrc/foo.tsx\nsrc/bar.tsx",
+ numFiles: 0,
+ });
+ });
+
it("maps WebSearch calls with structured results", () => {
const item = expectMapped(
mapClaudeCompletedToolCall({