Add fetch tool details and improve sidebar shortcuts handling
This commit is contained in:
@@ -147,6 +147,30 @@ export type ToolCallDetail =
|
||||
| {
|
||||
type: "search";
|
||||
query: string;
|
||||
toolName?: "search" | "grep" | "glob" | "web_search";
|
||||
content?: string;
|
||||
filePaths?: string[];
|
||||
webResults?: Array<{
|
||||
title: string;
|
||||
url: string;
|
||||
}>;
|
||||
annotations?: string[];
|
||||
numFiles?: number;
|
||||
numMatches?: number;
|
||||
durationMs?: number;
|
||||
durationSeconds?: number;
|
||||
truncated?: boolean;
|
||||
mode?: "content" | "files_with_matches" | "count";
|
||||
}
|
||||
| {
|
||||
type: "fetch";
|
||||
url: string;
|
||||
prompt?: string;
|
||||
result?: string;
|
||||
code?: number;
|
||||
codeText?: string;
|
||||
bytes?: number;
|
||||
durationMs?: number;
|
||||
}
|
||||
| {
|
||||
type: "worktree_setup";
|
||||
|
||||
@@ -7,11 +7,17 @@ import {
|
||||
ToolReadInputSchema,
|
||||
ToolReadOutputSchema,
|
||||
ToolSearchInputSchema,
|
||||
ToolGrepOutputSchema,
|
||||
ToolGlobOutputSchema,
|
||||
ToolShellInputSchema,
|
||||
ToolShellOutputSchema,
|
||||
ToolWebFetchInputSchema,
|
||||
ToolWebFetchOutputSchema,
|
||||
ToolWebSearchOutputSchema,
|
||||
ToolWriteInputSchema,
|
||||
ToolWriteOutputSchema,
|
||||
toEditToolDetail,
|
||||
toFetchToolDetail,
|
||||
toReadToolDetail,
|
||||
toSearchToolDetail,
|
||||
toShellToolDetail,
|
||||
@@ -87,27 +93,32 @@ const ClaudeToolDetailPass2Schema = z.union([
|
||||
ToolEditOutputSchema,
|
||||
toEditToolDetail
|
||||
),
|
||||
toolDetailBranchByName("WebSearch", ToolSearchInputSchema, z.unknown(), (input) =>
|
||||
toSearchToolDetail(input)
|
||||
toolDetailBranchByName("WebSearch", ToolSearchInputSchema, ToolWebSearchOutputSchema.nullable(), (input, output) =>
|
||||
toSearchToolDetail({ input, output, toolName: "web_search" })
|
||||
),
|
||||
toolDetailBranchByName("web_search", ToolSearchInputSchema, z.unknown(), (input) =>
|
||||
toSearchToolDetail(input)
|
||||
toolDetailBranchByName("web_search", ToolSearchInputSchema, ToolWebSearchOutputSchema.nullable(), (input, output) =>
|
||||
toSearchToolDetail({ input, output, toolName: "web_search" })
|
||||
),
|
||||
toolDetailBranchByName("search", ToolSearchInputSchema, z.unknown(), (input) =>
|
||||
toSearchToolDetail(input)
|
||||
toSearchToolDetail({ input, toolName: "search" })
|
||||
),
|
||||
toolDetailBranchByName("Grep", ToolSearchInputSchema, z.unknown(), (input) =>
|
||||
toSearchToolDetail(input)
|
||||
toolDetailBranchByName("Grep", ToolSearchInputSchema, ToolGrepOutputSchema.nullable(), (input, output) =>
|
||||
toSearchToolDetail({ input, output, toolName: "grep" })
|
||||
),
|
||||
toolDetailBranchByName("grep", ToolSearchInputSchema, z.unknown(), (input) =>
|
||||
toSearchToolDetail(input)
|
||||
toolDetailBranchByName("grep", ToolSearchInputSchema, ToolGrepOutputSchema.nullable(), (input, output) =>
|
||||
toSearchToolDetail({ input, output, toolName: "grep" })
|
||||
),
|
||||
toolDetailBranchByName("Glob", ToolSearchInputSchema, z.unknown(), (input) =>
|
||||
toSearchToolDetail(input)
|
||||
toolDetailBranchByName("Glob", ToolSearchInputSchema, ToolGlobOutputSchema.nullable(), (input, output) =>
|
||||
toSearchToolDetail({ input, output, toolName: "glob" })
|
||||
),
|
||||
toolDetailBranchByName("glob", ToolSearchInputSchema, z.unknown(), (input) =>
|
||||
toSearchToolDetail(input)
|
||||
toolDetailBranchByName("glob", ToolSearchInputSchema, ToolGlobOutputSchema.nullable(), (input, output) =>
|
||||
toSearchToolDetail({ input, output, toolName: "glob" })
|
||||
),
|
||||
toolDetailBranchByName("WebFetch", ToolWebFetchInputSchema, ToolWebFetchOutputSchema, toFetchToolDetail),
|
||||
toolDetailBranchByName("web_fetch", ToolWebFetchInputSchema, ToolWebFetchOutputSchema, toFetchToolDetail),
|
||||
toolDetailBranchByName("WebFetchTool", ToolWebFetchInputSchema, ToolWebFetchOutputSchema, toFetchToolDetail),
|
||||
toolDetailBranchByName("web_fetch_tool", ToolWebFetchInputSchema, ToolWebFetchOutputSchema, toFetchToolDetail),
|
||||
toolDetailBranchByName("webfetch", ToolWebFetchInputSchema, ToolWebFetchOutputSchema, toFetchToolDetail),
|
||||
toolDetailBranchByName(
|
||||
"Skill",
|
||||
z.object({ skill: z.string() }).passthrough(),
|
||||
|
||||
@@ -103,6 +103,7 @@ describe("claude tool-call mapper", () => {
|
||||
expect(searchItem.detail).toEqual({
|
||||
type: "search",
|
||||
query: "tool call mapping",
|
||||
toolName: "web_search",
|
||||
});
|
||||
});
|
||||
|
||||
@@ -220,6 +221,7 @@ describe("claude tool-call mapper", () => {
|
||||
expect(searchItem.detail).toEqual({
|
||||
type: "search",
|
||||
query: "tool call mapping",
|
||||
toolName: "web_search",
|
||||
});
|
||||
});
|
||||
|
||||
@@ -248,7 +250,12 @@ describe("claude tool-call mapper", () => {
|
||||
callId: "claude-glob-1",
|
||||
name: "Glob",
|
||||
input: { pattern: "**/.claude/commands/paseo*" },
|
||||
output: { output: "No files found" },
|
||||
output: {
|
||||
durationMs: 7,
|
||||
numFiles: 2,
|
||||
filenames: ["a.txt", "b.txt"],
|
||||
truncated: false,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
@@ -258,6 +265,11 @@ describe("claude tool-call mapper", () => {
|
||||
expect(item.detail).toEqual({
|
||||
type: "search",
|
||||
query: "**/.claude/commands/paseo*",
|
||||
toolName: "glob",
|
||||
filePaths: ["a.txt", "b.txt"],
|
||||
numFiles: 2,
|
||||
durationMs: 7,
|
||||
truncated: false,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -272,7 +284,14 @@ describe("claude tool-call mapper", () => {
|
||||
output_mode: "content",
|
||||
"-n": true,
|
||||
},
|
||||
output: { output: "No matches found" },
|
||||
output: {
|
||||
mode: "content",
|
||||
numFiles: 1,
|
||||
filenames: ["src/main.rs"],
|
||||
content: "12:const cli = true;",
|
||||
numLines: 1,
|
||||
numMatches: 1,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
@@ -282,6 +301,80 @@ describe("claude tool-call mapper", () => {
|
||||
expect(item.detail).toEqual({
|
||||
type: "search",
|
||||
query: '\\\\\\"cli\\\\\\""',
|
||||
toolName: "grep",
|
||||
content: "12:const cli = true;",
|
||||
filePaths: ["src/main.rs"],
|
||||
numFiles: 1,
|
||||
numMatches: 1,
|
||||
mode: "content",
|
||||
});
|
||||
});
|
||||
|
||||
it("maps WebSearch calls with structured results", () => {
|
||||
const item = expectMapped(
|
||||
mapClaudeCompletedToolCall({
|
||||
callId: "claude-web-search-1",
|
||||
name: "WebSearch",
|
||||
input: { query: "OpenAI latest news" },
|
||||
output: {
|
||||
query: "OpenAI latest news",
|
||||
results: [
|
||||
"Top results:",
|
||||
{
|
||||
tool_use_id: "toolu_123",
|
||||
content: [
|
||||
{ title: "OpenAI launches thing", url: "https://example.com/1" },
|
||||
{ title: "Another result", url: "https://example.com/2" },
|
||||
],
|
||||
},
|
||||
],
|
||||
durationSeconds: 1.5,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
expect(item.detail).toEqual({
|
||||
type: "search",
|
||||
query: "OpenAI latest news",
|
||||
toolName: "web_search",
|
||||
webResults: [
|
||||
{ title: "OpenAI launches thing", url: "https://example.com/1" },
|
||||
{ title: "Another result", url: "https://example.com/2" },
|
||||
],
|
||||
annotations: ["Top results:"],
|
||||
durationSeconds: 1.5,
|
||||
});
|
||||
});
|
||||
|
||||
it("maps WebFetch calls with fetched content", () => {
|
||||
const item = expectMapped(
|
||||
mapClaudeCompletedToolCall({
|
||||
callId: "claude-web-fetch-1",
|
||||
name: "WebFetch",
|
||||
input: {
|
||||
url: "https://example.com/article",
|
||||
prompt: "Summarize this page",
|
||||
},
|
||||
output: {
|
||||
bytes: 5120,
|
||||
code: 200,
|
||||
codeText: "OK",
|
||||
result: "Summary text",
|
||||
durationMs: 250,
|
||||
url: "https://example.com/article",
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
expect(item.detail).toEqual({
|
||||
type: "fetch",
|
||||
url: "https://example.com/article",
|
||||
prompt: "Summarize this page",
|
||||
result: "Summary text",
|
||||
code: 200,
|
||||
codeText: "OK",
|
||||
bytes: 5120,
|
||||
durationMs: 250,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -80,6 +80,13 @@ const ClaudeSearchToolNameSchema = z.union([
|
||||
z.literal("Glob"),
|
||||
z.literal("glob"),
|
||||
]);
|
||||
const ClaudeFetchToolNameSchema = z.union([
|
||||
z.literal("WebFetch"),
|
||||
z.literal("web_fetch"),
|
||||
z.literal("WebFetchTool"),
|
||||
z.literal("web_fetch_tool"),
|
||||
z.literal("webfetch"),
|
||||
]);
|
||||
const ClaudeSpeakToolNameSchema = z
|
||||
.string()
|
||||
.min(1)
|
||||
@@ -91,6 +98,7 @@ const ClaudeToolKindSchema = z.enum([
|
||||
"write",
|
||||
"edit",
|
||||
"search",
|
||||
"fetch",
|
||||
"speak",
|
||||
"unknown",
|
||||
]);
|
||||
@@ -146,6 +154,13 @@ const ClaudeToolCallPass2EnvelopeSchema = z.union([
|
||||
name: normalized.name.trim(),
|
||||
toolKind: "search" as const,
|
||||
})),
|
||||
ClaudeToolCallPass2InputSchema.extend({
|
||||
name: ClaudeFetchToolNameSchema,
|
||||
}).transform((normalized) => ({
|
||||
...normalized,
|
||||
name: normalized.name.trim(),
|
||||
toolKind: "fetch" as const,
|
||||
})),
|
||||
ClaudeToolCallPass2InputSchema.extend({
|
||||
name: ClaudeSpeakToolNameSchema,
|
||||
}).transform((normalized) => ({
|
||||
@@ -181,6 +196,10 @@ const ClaudeToolCallPass2Schema = z.discriminatedUnion("toolKind", [
|
||||
toolKind: z.literal("search"),
|
||||
name: ClaudeSearchToolNameSchema,
|
||||
}),
|
||||
ClaudeToolCallPass2BaseSchema.extend({
|
||||
toolKind: z.literal("fetch"),
|
||||
name: ClaudeFetchToolNameSchema,
|
||||
}),
|
||||
ClaudeToolCallPass2BaseSchema.extend({
|
||||
toolKind: z.literal("speak"),
|
||||
name: z.literal("speak"),
|
||||
@@ -203,8 +222,8 @@ function toToolCallTimelineItem(normalized: ClaudeToolCallPass2): ToolCallTimeli
|
||||
? "write_file"
|
||||
: normalized.toolKind === "edit"
|
||||
? "apply_patch"
|
||||
: normalized.toolKind === "search"
|
||||
? "search"
|
||||
: normalized.toolKind === "search" || normalized.toolKind === "fetch"
|
||||
? normalized.name
|
||||
: normalized.toolKind === "speak"
|
||||
? "speak"
|
||||
: normalized.name;
|
||||
|
||||
@@ -135,6 +135,10 @@ function normalizeCodexModelId(modelId: string | null | undefined): string | und
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function normalizeCodexModelLabel(displayName: string): string {
|
||||
return displayName.replace(/\bgpt\b/gi, 'GPT');
|
||||
}
|
||||
|
||||
type CodexConfiguredDefaults = {
|
||||
model?: string;
|
||||
thinkingOptionId?: string;
|
||||
@@ -3389,7 +3393,7 @@ export class CodexAppServerAgentClient implements AgentClient {
|
||||
return {
|
||||
provider: CODEX_PROVIDER,
|
||||
id: model.id,
|
||||
label: model.displayName,
|
||||
label: normalizeCodexModelLabel(model.displayName),
|
||||
description: model.description,
|
||||
isDefault: isDefaultModel,
|
||||
thinkingOptions: thinkingOptions.length > 0 ? thinkingOptions : undefined,
|
||||
|
||||
@@ -150,10 +150,10 @@ const CodexToolDetailPass2Schema = z.union([
|
||||
(input, output, cwd) => toEditToolDetail(input, output, { normalizePath: normalizePathForCwd(cwd) })
|
||||
),
|
||||
toolDetailBranchByNameWithCwd("search", ToolSearchInputSchema, z.unknown(), (input) =>
|
||||
toSearchToolDetail(input)
|
||||
toSearchToolDetail({ input, toolName: "search" })
|
||||
),
|
||||
toolDetailBranchByNameWithCwd("web_search", ToolSearchInputSchema, z.unknown(), (input) =>
|
||||
toSearchToolDetail(input)
|
||||
toSearchToolDetail({ input, toolName: "web_search" })
|
||||
),
|
||||
CodexSpeakToolDetailSchema,
|
||||
]);
|
||||
|
||||
@@ -151,6 +151,7 @@ describe("codex tool-call mapper", () => {
|
||||
expect(searchItem?.detail).toEqual({
|
||||
type: "search",
|
||||
query: "codex timeline",
|
||||
toolName: "web_search",
|
||||
});
|
||||
});
|
||||
|
||||
@@ -338,6 +339,7 @@ describe("codex tool-call mapper", () => {
|
||||
expect(searchItem?.detail).toEqual({
|
||||
type: "search",
|
||||
query: "codex timeline",
|
||||
toolName: "web_search",
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -38,10 +38,10 @@ const OpencodeKnownToolDetailSchema = z.union([
|
||||
toolDetailBranchByToolName("apply_patch", ToolEditInputSchema, ToolEditOutputSchema, toEditToolDetail),
|
||||
toolDetailBranchByToolName("apply_diff", ToolEditInputSchema, ToolEditOutputSchema, toEditToolDetail),
|
||||
toolDetailBranchByToolName("search", ToolSearchInputSchema, z.unknown(), (input) =>
|
||||
toSearchToolDetail(input)
|
||||
toSearchToolDetail({ input, toolName: "search" })
|
||||
),
|
||||
toolDetailBranchByToolName("web_search", ToolSearchInputSchema, z.unknown(), (input) =>
|
||||
toSearchToolDetail(input)
|
||||
toSearchToolDetail({ input, toolName: "web_search" })
|
||||
),
|
||||
]);
|
||||
|
||||
|
||||
@@ -86,6 +86,7 @@ describe("opencode tool-call mapper", () => {
|
||||
expect(searchItem.detail).toEqual({
|
||||
type: "search",
|
||||
query: "opencode mapper",
|
||||
toolName: "web_search",
|
||||
});
|
||||
});
|
||||
|
||||
@@ -210,6 +211,7 @@ describe("opencode tool-call mapper", () => {
|
||||
expect(searchItem.detail).toEqual({
|
||||
type: "search",
|
||||
query: "opencode mapper",
|
||||
toolName: "web_search",
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -512,6 +512,71 @@ export const ToolSearchInputSchema = z.union([
|
||||
z.object({ pattern: z.string() }).passthrough().transform((value) => ({ query: value.pattern })),
|
||||
]);
|
||||
|
||||
export const ToolGlobOutputSchema = z
|
||||
.object({
|
||||
durationMs: z.number().finite(),
|
||||
numFiles: z.number().int().nonnegative(),
|
||||
filenames: z.array(z.string()),
|
||||
truncated: z.boolean(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export const ToolGrepOutputSchema = z
|
||||
.object({
|
||||
mode: z.enum(["content", "files_with_matches", "count"]).optional(),
|
||||
numFiles: z.number().int().nonnegative(),
|
||||
filenames: z.array(z.string()),
|
||||
content: z.string().optional(),
|
||||
numLines: z.number().int().nonnegative().optional(),
|
||||
numMatches: z.number().int().nonnegative().optional(),
|
||||
appliedLimit: z.number().int().nonnegative().optional(),
|
||||
appliedOffset: z.number().int().nonnegative().optional(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export const ToolWebFetchInputSchema = z
|
||||
.object({
|
||||
url: z.string(),
|
||||
prompt: z.string(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export const ToolWebFetchOutputSchema = z
|
||||
.object({
|
||||
bytes: z.number().int().nonnegative(),
|
||||
code: z.number().int(),
|
||||
codeText: z.string(),
|
||||
result: z.string(),
|
||||
durationMs: z.number().finite(),
|
||||
url: z.string(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
const ToolWebSearchResultHitSchema = z
|
||||
.object({
|
||||
title: z.string(),
|
||||
url: z.string(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
const ToolWebSearchResultEntrySchema = z.union([
|
||||
z
|
||||
.object({
|
||||
tool_use_id: z.string(),
|
||||
content: z.array(ToolWebSearchResultHitSchema),
|
||||
})
|
||||
.passthrough(),
|
||||
z.string(),
|
||||
]);
|
||||
|
||||
export const ToolWebSearchOutputSchema = z
|
||||
.object({
|
||||
query: z.string(),
|
||||
results: z.array(ToolWebSearchResultEntrySchema),
|
||||
durationSeconds: z.number().finite(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export type ParsedToolShellInput = z.infer<typeof ToolShellInputSchema>;
|
||||
export type ParsedToolShellOutput = z.infer<typeof ToolShellOutputSchema>;
|
||||
export type ParsedToolReadInput = z.infer<typeof ToolReadInputSchema>;
|
||||
@@ -522,6 +587,11 @@ export type ParsedToolWriteOutput = z.infer<typeof ToolWriteOutputSchema>;
|
||||
export type ParsedToolEditInput = z.infer<typeof ToolEditInputSchema>;
|
||||
export type ParsedToolEditOutput = z.infer<typeof ToolEditOutputSchema>;
|
||||
export type ParsedToolSearchInput = z.infer<typeof ToolSearchInputSchema>;
|
||||
export type ParsedToolGlobOutput = z.infer<typeof ToolGlobOutputSchema>;
|
||||
export type ParsedToolGrepOutput = z.infer<typeof ToolGrepOutputSchema>;
|
||||
export type ParsedToolWebFetchInput = z.infer<typeof ToolWebFetchInputSchema>;
|
||||
export type ParsedToolWebFetchOutput = z.infer<typeof ToolWebFetchOutputSchema>;
|
||||
export type ParsedToolWebSearchOutput = z.infer<typeof ToolWebSearchOutputSchema>;
|
||||
|
||||
type NormalizePathFn = (filePath: string) => string | undefined;
|
||||
|
||||
@@ -539,6 +609,24 @@ function normalizeDetailPath(
|
||||
return normalizePath ? normalizePath(trimmed) : trimmed;
|
||||
}
|
||||
|
||||
function isParsedToolGlobOutput(
|
||||
output: ParsedToolGrepOutput | ParsedToolGlobOutput | ParsedToolWebSearchOutput | null | undefined
|
||||
): output is ParsedToolGlobOutput {
|
||||
return Boolean(output && "truncated" in output);
|
||||
}
|
||||
|
||||
function isParsedToolGrepOutput(
|
||||
output: ParsedToolGrepOutput | ParsedToolGlobOutput | ParsedToolWebSearchOutput | null | undefined
|
||||
): output is ParsedToolGrepOutput {
|
||||
return Boolean(output && "filenames" in output && !("truncated" in output));
|
||||
}
|
||||
|
||||
function isParsedToolWebSearchOutput(
|
||||
output: ParsedToolGrepOutput | ParsedToolGlobOutput | ParsedToolWebSearchOutput | null | undefined
|
||||
): output is ParsedToolWebSearchOutput {
|
||||
return Boolean(output && "results" in output);
|
||||
}
|
||||
|
||||
export function toShellToolDetail(
|
||||
input: ParsedToolShellInput | null,
|
||||
output: ParsedToolShellOutput | null
|
||||
@@ -625,15 +713,70 @@ export function toEditToolDetail(
|
||||
};
|
||||
}
|
||||
|
||||
export function toSearchToolDetail(
|
||||
input: ParsedToolSearchInput | null
|
||||
): ToolCallDetail | undefined {
|
||||
export function toSearchToolDetail(params: {
|
||||
input: ParsedToolSearchInput | null;
|
||||
output?: ParsedToolGrepOutput | ParsedToolGlobOutput | ParsedToolWebSearchOutput | null;
|
||||
toolName?: "search" | "grep" | "glob" | "web_search";
|
||||
}): ToolCallDetail | undefined {
|
||||
const { input, output, toolName } = params;
|
||||
if (!input?.query) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const filePaths =
|
||||
isParsedToolGrepOutput(output) || isParsedToolGlobOutput(output)
|
||||
? output.filenames.length > 0
|
||||
? output.filenames
|
||||
: undefined
|
||||
: undefined;
|
||||
const webResults = isParsedToolWebSearchOutput(output)
|
||||
? output.results.flatMap((entry) => (typeof entry === "string" ? [] : entry.content))
|
||||
: undefined;
|
||||
const annotations = isParsedToolWebSearchOutput(output)
|
||||
? output.results.filter((entry): entry is string => typeof entry === "string")
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
type: "search",
|
||||
query: input.query,
|
||||
...(toolName ? { toolName } : {}),
|
||||
...(isParsedToolGrepOutput(output) && output.content ? { content: output.content } : {}),
|
||||
...(filePaths ? { filePaths } : {}),
|
||||
...(webResults && webResults.length > 0 ? { webResults } : {}),
|
||||
...(annotations && annotations.length > 0 ? { annotations } : {}),
|
||||
...(isParsedToolGrepOutput(output) || isParsedToolGlobOutput(output)
|
||||
? { numFiles: output.numFiles }
|
||||
: {}),
|
||||
...(isParsedToolGrepOutput(output) && output.numMatches !== undefined
|
||||
? { numMatches: output.numMatches }
|
||||
: {}),
|
||||
...(isParsedToolGlobOutput(output) ? { durationMs: output.durationMs } : {}),
|
||||
...(isParsedToolWebSearchOutput(output)
|
||||
? { durationSeconds: output.durationSeconds }
|
||||
: {}),
|
||||
...(isParsedToolGlobOutput(output) ? { truncated: output.truncated } : {}),
|
||||
...(isParsedToolGrepOutput(output) && output.mode ? { mode: output.mode } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
export function toFetchToolDetail(
|
||||
input: ParsedToolWebFetchInput | null,
|
||||
output: ParsedToolWebFetchOutput | null
|
||||
): ToolCallDetail | undefined {
|
||||
const url = input?.url ?? output?.url;
|
||||
if (!url) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
type: "fetch",
|
||||
url,
|
||||
...(input?.prompt ? { prompt: input.prompt } : {}),
|
||||
...(output?.result ? { result: output.result } : {}),
|
||||
...(output?.code !== undefined ? { code: output.code } : {}),
|
||||
...(output?.codeText ? { codeText: output.codeText } : {}),
|
||||
...(output?.bytes !== undefined ? { bytes: output.bytes } : {}),
|
||||
...(output?.durationMs !== undefined ? { durationMs: output.durationMs } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -181,6 +181,32 @@ const ToolCallDetailPayloadSchema: z.ZodType<ToolCallDetail> = z.discriminatedUn
|
||||
z.object({
|
||||
type: z.literal('search'),
|
||||
query: z.string(),
|
||||
toolName: z.enum(['search', 'grep', 'glob', 'web_search']).optional(),
|
||||
content: z.string().optional(),
|
||||
filePaths: z.array(z.string()).optional(),
|
||||
webResults: z.array(
|
||||
z.object({
|
||||
title: z.string(),
|
||||
url: z.string(),
|
||||
})
|
||||
).optional(),
|
||||
annotations: z.array(z.string()).optional(),
|
||||
numFiles: z.number().optional(),
|
||||
numMatches: z.number().optional(),
|
||||
durationMs: z.number().optional(),
|
||||
durationSeconds: z.number().optional(),
|
||||
truncated: z.boolean().optional(),
|
||||
mode: z.enum(['content', 'files_with_matches', 'count']).optional(),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal('fetch'),
|
||||
url: z.string(),
|
||||
prompt: z.string().optional(),
|
||||
result: z.string().optional(),
|
||||
code: z.number().optional(),
|
||||
codeText: z.string().optional(),
|
||||
bytes: z.number().optional(),
|
||||
durationMs: z.number().optional(),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal('worktree_setup'),
|
||||
|
||||
@@ -90,6 +90,11 @@ function buildCanonicalDetailDisplay(input: ToolCallDisplayInput): DetailDisplay
|
||||
displayName: "Search",
|
||||
summary: input.detail.query,
|
||||
};
|
||||
case "fetch":
|
||||
return {
|
||||
displayName: "Fetch",
|
||||
summary: input.detail.url,
|
||||
};
|
||||
case "worktree_setup":
|
||||
return {
|
||||
displayName: "Worktree Setup",
|
||||
|
||||
Reference in New Issue
Block a user