Skip to content
This repository was archived by the owner on Jun 8, 2026. It is now read-only.

Commit b59b75a

Browse files
committed
PLAN-73: Add descriptive context to tool call and result summaries
- Tool calls now include key input (file path, command, pattern) e.g. 'Tool: Read(/src/server/app.ts)' instead of 'Tool: Read' - Tool results now include a truncated preview of the output e.g. 'Tool result: found 3 matches...' instead of 'Tool result' Testing: All 18 tests pass, lint and typecheck clean Risks: None identified
1 parent b6b2a89 commit b59b75a

1 file changed

Lines changed: 30 additions & 5 deletions

File tree

apps/desktop/src/server/operations/output-tailer.ts

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ export function isRecord(v: unknown): v is Record<string, unknown> {
1010
// ---------------------------------------------------------------------------
1111

1212
type TextBlock = { type: "text"; text: string };
13-
type ToolUseBlock = { type: "tool_use"; name: string };
13+
type ToolUseBlock = { type: "tool_use"; name: string; input?: Record<string, unknown> };
1414
type ThinkingBlock = { type: "thinking" };
15-
type ToolResultBlock = { type: "tool_result"; is_error?: boolean };
15+
type ToolResultBlock = { type: "tool_result"; is_error?: boolean; content?: string | unknown[] };
1616

1717
type ContentBlock = TextBlock | ToolUseBlock | ThinkingBlock | ToolResultBlock;
1818

@@ -54,6 +54,28 @@ function redactSensitive(input: string): string {
5454
.replace(/-----BEGIN [A-Z ]+ KEY-----/g, "[REDACTED]");
5555
}
5656

57+
function summarizeToolInput(name: string, input: Record<string, unknown>): string {
58+
const filePath = input.file_path ?? input.path;
59+
if (typeof filePath === "string") return `Tool: ${name}(${truncate(filePath, 80)})`;
60+
if (typeof input.command === "string") return `Tool: ${name}(${truncate(input.command, 80)})`;
61+
if (typeof input.pattern === "string") return `Tool: ${name}(${truncate(input.pattern, 80)})`;
62+
return `Tool: ${name}`;
63+
}
64+
65+
function summarizeToolResult(block: ToolResultBlock): string {
66+
if (block.is_error === true) return "Tool error";
67+
const content = block.content;
68+
if (typeof content === "string" && content.length > 0) return `Tool result: ${truncate(content, 120)}`;
69+
if (Array.isArray(content)) {
70+
for (const part of content) {
71+
if (isRecord(part) && part.type === "text" && typeof part.text === "string") {
72+
return `Tool result: ${truncate(part.text, 120)}`;
73+
}
74+
}
75+
}
76+
return "Tool result";
77+
}
78+
5779
/** Accepts a parsed JSONL record (untrusted) and returns a display summary, or null to skip. */
5880
export function summarizeJsonlRecord(record: Record<string, unknown>): string | null {
5981
const typed = record as JsonlRecord;
@@ -67,14 +89,17 @@ export function summarizeJsonlRecord(record: Record<string, unknown>): string |
6789
for (const block of content) {
6890
if (!isRecord(block)) continue;
6991
switch (block.type) {
70-
case "tool_use":
71-
return redactSensitive(`Tool: ${String((block as ToolUseBlock).name ?? "unknown")}`);
92+
case "tool_use": {
93+
const b = block as ToolUseBlock;
94+
const input = isRecord(b.input) ? b.input : {};
95+
return redactSensitive(summarizeToolInput(String(b.name ?? "unknown"), input));
96+
}
7297
case "text":
7398
return redactSensitive(truncate(String((block as TextBlock).text ?? ""), 200));
7499
case "thinking":
75100
return redactSensitive("Thinking...");
76101
case "tool_result":
77-
return redactSensitive((block as ToolResultBlock).is_error === true ? "Tool error" : "Tool result");
102+
return redactSensitive(summarizeToolResult(block as ToolResultBlock));
78103
}
79104
}
80105
return null;

0 commit comments

Comments
 (0)