From 692821ffc882b16e5beaccc70909e86ea7176bb1 Mon Sep 17 00:00:00 2001 From: Alejandro Tamayo Date: Fri, 24 Apr 2026 18:02:09 +0200 Subject: [PATCH] fix(agents): render subagent dispatch rows regardless of SDK tool name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Agent SDK's CLI-parity built-in subagents (shipped in #36) can emit the dispatch tool as either `Task` or `Agent`. The inline chat renderer hard-coded `tool-Task`, so `tool-Agent` dispatches fell through to the generic fallback that shows only the bare tool name with no description, while the Tasks sidebar rendered correctly because it reads input by shape. Result: the top row looked empty ("Agent" with no subtitle) even though the subagent was running and nested child tools rendered fine. - Add `isSubagentDispatchType()` helper; use it at the primary and orphan-group dispatch sites so `AgentTaskTool` handles both names. - Harden the catch-all fallback to show ` ` via a shared `summarizeToolInput()` (extracted from tasks-widget so the two surfaces stay in sync). - Bump `@anthropic-ai/claude-agent-sdk` 0.2.118 → 0.2.119 and the bundled CLI binary 2.1.118 → 2.1.119 (released in lockstep). Co-Authored-By: Claude Opus 4.7 (1M context) --- bun.lock | 20 ++++----- package.json | 6 +-- .../agents/main/assistant-message-item.tsx | 26 +++++++++--- .../features/agents/ui/agent-tool-utils.ts | 41 +++++++++++++++++++ .../details-sidebar/sections/tasks-widget.tsx | 28 +------------ 5 files changed, 77 insertions(+), 44 deletions(-) diff --git a/bun.lock b/bun.lock index 22275e89b..39e807445 100644 --- a/bun.lock +++ b/bun.lock @@ -6,7 +6,7 @@ "name": "21st-desktop", "dependencies": { "@ai-sdk/react": "^3.0.14", - "@anthropic-ai/claude-agent-sdk": "0.2.118", + "@anthropic-ai/claude-agent-sdk": "0.2.119", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", @@ -131,23 +131,23 @@ "@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="], - "@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.118", "", { "dependencies": { "@anthropic-ai/sdk": "^0.81.0", "@modelcontextprotocol/sdk": "^1.29.0" }, "optionalDependencies": { "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.2.118", "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.2.118", "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.2.118", "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.2.118", "@anthropic-ai/claude-agent-sdk-linux-x64": "0.2.118", "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.2.118", "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.2.118", "@anthropic-ai/claude-agent-sdk-win32-x64": "0.2.118" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-OfxCTzmfqvctpTLd3CP+UrpC0JdhYcJp12rD+SK29k+9+hrbblCrLobvhdWpTuYFejTPJuiLVsbHxq0BkEuELQ=="], + "@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.2.119", "", { "dependencies": { "@anthropic-ai/sdk": "^0.81.0", "@modelcontextprotocol/sdk": "^1.29.0" }, "optionalDependencies": { "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.2.119", "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.2.119", "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.2.119", "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.2.119", "@anthropic-ai/claude-agent-sdk-linux-x64": "0.2.119", "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.2.119", "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.2.119", "@anthropic-ai/claude-agent-sdk-win32-x64": "0.2.119" }, "peerDependencies": { "zod": "^4.0.0" } }, "sha512-6AvthpsaOTlkn514brSGOcCSLHDXODnU+ExN1O3CJCjxr5RBcmzR057C9EIM0G7IchnXsRfMZgRO1QKsjTXdbA=="], - "@anthropic-ai/claude-agent-sdk-darwin-arm64": ["@anthropic-ai/claude-agent-sdk-darwin-arm64@0.2.118", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RudnoBekv0c9CPL0EeMc4RqDe4Pb7tdz/2oxa5EYqaajXNRlYtTvru9q7wq7Zvp40JQ24hz38swOTJ7PkW7G/g=="], + "@anthropic-ai/claude-agent-sdk-darwin-arm64": ["@anthropic-ai/claude-agent-sdk-darwin-arm64@0.2.119", "", { "os": "darwin", "cpu": "arm64" }, "sha512-kxnG37SZqUata2Jcp/YQ0n9Y7o/sinE/8LdG4ltM1gePh+z+0Mfa4vBUUTEBMBFth9PTovKoesIuVuyFpvO/Cw=="], - "@anthropic-ai/claude-agent-sdk-darwin-x64": ["@anthropic-ai/claude-agent-sdk-darwin-x64@0.2.118", "", { "os": "darwin", "cpu": "x64" }, "sha512-Hf/H46uElpfygALlb4KZR2EuyyJRe7jBuWa+TDA4jmAHVblNfwkVyaCp8s61hZINB3kAmXdLdM81VI+xwruWzA=="], + "@anthropic-ai/claude-agent-sdk-darwin-x64": ["@anthropic-ai/claude-agent-sdk-darwin-x64@0.2.119", "", { "os": "darwin", "cpu": "x64" }, "sha512-9Aj8g3ELsmZuOFg17TCkikeg/Wt2ucVT8hOOPQUatzLd7BKhydrHLA0RP42nBpWECO1B/n/mPdQ4iS/LS3s2Fg=="], - "@anthropic-ai/claude-agent-sdk-linux-arm64": ["@anthropic-ai/claude-agent-sdk-linux-arm64@0.2.118", "", { "os": "linux", "cpu": "arm64" }, "sha512-lwMXnweJKpzESezJFM8mngRxJfaq/N0gqyFXBm5bOYaPIZnlGlP3h1JMKsJeqC4neLVGbe5a3Hq4T22Rr7OoAA=="], + "@anthropic-ai/claude-agent-sdk-linux-arm64": ["@anthropic-ai/claude-agent-sdk-linux-arm64@0.2.119", "", { "os": "linux", "cpu": "arm64" }, "sha512-v3o464XkiYehp/OKidQQirxdVb+aGSvdJvHF2zH9p33W8M/NC21zwwh4dhwDnKsyrtBIgkt2CcMwzIl30r0OtA=="], - "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": ["@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.2.118", "", { "os": "linux", "cpu": "arm64" }, "sha512-gSuZS8GM8MZuklzAJS8VCCjqK2UJJeerV+JpVYzXNMelotq4sXUg2dp17VbjCJ1jhUC9u1gpzlQDWkmYrXCbOg=="], + "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": ["@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.2.119", "", { "os": "linux", "cpu": "arm64" }, "sha512-IPGWgtz+gGnD7fxKAvSf913EUT/lYBTBE8EZ7lh3+x5ZP2859LWLmrCm053Lf3nMWo/CWikZsVPwkDVwpz6tIQ=="], - "@anthropic-ai/claude-agent-sdk-linux-x64": ["@anthropic-ai/claude-agent-sdk-linux-x64@0.2.118", "", { "os": "linux", "cpu": "x64" }, "sha512-m0KBbwN9s0+hQwAPzeUFvegrEqoT9EOC+Vz3vr4dd9FcZyvKZE0yiv9S7YbFp1ZKWDQmppmvpcB+9eME7WQ0yA=="], + "@anthropic-ai/claude-agent-sdk-linux-x64": ["@anthropic-ai/claude-agent-sdk-linux-x64@0.2.119", "", { "os": "linux", "cpu": "x64" }, "sha512-9ePt4ZN+hsqDw4AgS4KtcWIGKfL9Oq28kwkrTER/QAcSrVKxiLonp81cCLzg7Ok/IUJu4Cfd71GZbFv/WE54zw=="], - "@anthropic-ai/claude-agent-sdk-linux-x64-musl": ["@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.2.118", "", { "os": "linux", "cpu": "x64" }, "sha512-36lG1F9IsuNBV7AzJY98z8KwryoWZCeEtMzgZL7614zPBhZGBsziQUZEBm2Eu7FVWbRQmYv6BL52+gffpkM4Gw=="], + "@anthropic-ai/claude-agent-sdk-linux-x64-musl": ["@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.2.119", "", { "os": "linux", "cpu": "x64" }, "sha512-QYxFNAe4FFridPkKhGlNcNBJ0TaIygWYyvfI9g4kX0i+RVbresUWuZVkWY06ioJ0fXoixFJ+HNQBMB7dLrIp8Q=="], - "@anthropic-ai/claude-agent-sdk-win32-arm64": ["@anthropic-ai/claude-agent-sdk-win32-arm64@0.2.118", "", { "os": "win32", "cpu": "arm64" }, "sha512-o30/SL084+a8wJ+5cgKM1BflxiBUEy+xEcEpZPW+zCFtiqY0b1Pr+K35ECsbKBrv+w5/0Byp4/CvCkP15Otsgw=="], + "@anthropic-ai/claude-agent-sdk-win32-arm64": ["@anthropic-ai/claude-agent-sdk-win32-arm64@0.2.119", "", { "os": "win32", "cpu": "arm64" }, "sha512-p/TjcKQvkCYtXGPlR+mdyNwqCmvRcQL34Wtq0yUZ+iqmI/eyCe59IJ3AZrE0EZoqmiAevEYzatPIt9sncC9uxw=="], - "@anthropic-ai/claude-agent-sdk-win32-x64": ["@anthropic-ai/claude-agent-sdk-win32-x64@0.2.118", "", { "os": "win32", "cpu": "x64" }, "sha512-TSqsVBUaZGgYMkjCZckXhPvmJDTS7C6VAl4IOeMVNB/oPINVFaobtVagjYvY0BFnlDCOzz6sb8puafHwcm7qQA=="], + "@anthropic-ai/claude-agent-sdk-win32-x64": ["@anthropic-ai/claude-agent-sdk-win32-x64@0.2.119", "", { "os": "win32", "cpu": "x64" }, "sha512-k98Ju0wtktm6FhqTE/cXlVr6K4kGqBolVjEGzeKkW6ZILc7124euwNapAvkQCwMAavAxS/ZnO3jdKMtHtwTVTA=="], "@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.81.0", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-D4K5PvEV6wPiRtVlVsJHIUhHAmOZ6IT/I9rKlTf84gR7GyyAurPJK7z9BOf/AZqC5d1DhYQGJNKRmV+q8dGhgw=="], diff --git a/package.json b/package.json index 38fa79c90..27820518f 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,8 @@ "dist": "electron-builder", "dist:manifest": "node scripts/generate-update-manifest.mjs", "dist:upload": "node scripts/upload-release.mjs", - "claude:download": "node scripts/download-claude-binary.mjs --version=2.1.118", - "claude:download:all": "node scripts/download-claude-binary.mjs --version=2.1.118 --all", + "claude:download": "node scripts/download-claude-binary.mjs --version=2.1.119", + "claude:download:all": "node scripts/download-claude-binary.mjs --version=2.1.119 --all", "codex:download": "node scripts/download-codex-binary.mjs --version=0.124.0", "codex:download:all": "node scripts/download-codex-binary.mjs --version=0.124.0 --all", "release": "rm -rf release && bun i && bun run claude:download && bun run codex:download && bun run build && bun run package:mac && bun run dist:manifest && ./scripts/upload-release-wrangler.sh", @@ -36,7 +36,7 @@ }, "dependencies": { "@ai-sdk/react": "^3.0.14", - "@anthropic-ai/claude-agent-sdk": "0.2.118", + "@anthropic-ai/claude-agent-sdk": "0.2.119", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", diff --git a/src/renderer/features/agents/main/assistant-message-item.tsx b/src/renderer/features/agents/main/assistant-message-item.tsx index 08971eb8f..4915c3ac8 100644 --- a/src/renderer/features/agents/main/assistant-message-item.tsx +++ b/src/renderer/features/agents/main/assistant-message-item.tsx @@ -22,7 +22,11 @@ import { AgentEditTool } from "../ui/agent-edit-tool" import { AgentExploringGroup } from "../ui/agent-exploring-group" import { AgentTaskToolsGroup } from "../ui/agent-task-tools" import { AgentPlanFileTool } from "../ui/agent-plan-file-tool" -import { isPlanFile } from "../ui/agent-tool-utils" +import { + isPlanFile, + isSubagentDispatchType, + summarizeToolInput, +} from "../ui/agent-tool-utils" import { AgentMessageUsage, type AgentMessageMetadata, @@ -518,7 +522,7 @@ export const AssistantMessageItem = memo(function AssistantMessageItem({ const nestedToolIds = new Set() const taskPartIds = new Set( messageParts - .filter((p: any) => p.type === "tool-Task" && p.toolCallId) + .filter((p: any) => isSubagentDispatchType(p.type) && p.toolCallId) .map((p: any) => p.toolCallId) ) const orphanTaskGroups = new Map() @@ -698,7 +702,7 @@ export const AssistantMessageItem = memo(function AssistantMessageItem({ ) } - if (part.type === "tool-Task") { + if (isSubagentDispatchType(part.type)) { const nestedTools = nestedToolsMap.get(part.toolCallId) || [] return } @@ -838,9 +842,21 @@ export const AssistantMessageItem = memo(function AssistantMessageItem({ } if (part.type?.startsWith("tool-")) { + const toolName = part.type.replace("tool-", "") + const summary = summarizeToolInput(part.input) return ( -
- {part.type.replace("tool-", "")} +
+ + {toolName} + + {summary && ( + + {summary} + + )}
) } diff --git a/src/renderer/features/agents/ui/agent-tool-utils.ts b/src/renderer/features/agents/ui/agent-tool-utils.ts index ea2e35e65..f3bbbe57a 100644 --- a/src/renderer/features/agents/ui/agent-tool-utils.ts +++ b/src/renderer/features/agents/ui/agent-tool-utils.ts @@ -196,6 +196,47 @@ export function areExploringGroupPropsEqual( return true } +/** + * Pick a short, human-readable summary string from a tool's input. + * Walks a priority list of keys (description first, subagent_type last) and + * returns the first non-empty string value. Used by both the inline chat + * fallback renderer and the live Tasks widget so they stay in sync. + */ +export function summarizeToolInput(input: unknown): string { + if (!input || typeof input !== "object") return "" + const rec = input as Record + const preferredKeys = [ + "command", + "file_path", + "path", + "pattern", + "description", + "url", + "query", + "prompt", + "subagent_type", + ] + for (const key of preferredKeys) { + const v = rec[key] + if (typeof v === "string" && v.length > 0) return v + } + for (const key in rec) { + const v = rec[key] + if (typeof v === "string" && v.length > 0) return v + } + return "" +} + +/** + * Shape-based check for the Claude Agent SDK's subagent-dispatch tool. + * Historically emitted as `tool-Task`; with CLI-parity built-in subagents the + * SDK can also emit it as `tool-Agent`. Treat both as the same dispatcher so + * the chat row stays consistent. + */ +export function isSubagentDispatchType(type: string | undefined): boolean { + return type === "tool-Task" || type === "tool-Agent" +} + /** * Check if a file path is a plan file. * Plan files are stored in the claude-sessions directory under /plans/ diff --git a/src/renderer/features/details-sidebar/sections/tasks-widget.tsx b/src/renderer/features/details-sidebar/sections/tasks-widget.tsx index 170df242a..6e7890c6f 100644 --- a/src/renderer/features/details-sidebar/sections/tasks-widget.tsx +++ b/src/renderer/features/details-sidebar/sections/tasks-widget.tsx @@ -12,6 +12,7 @@ import { type Message, } from "@/features/agents/stores/message-store" import { useStreamingStatusStore } from "@/features/agents/stores/streaming-status-store" +import { summarizeToolInput } from "@/features/agents/ui/agent-tool-utils" interface TasksWidgetProps { subChatId: string | null @@ -38,31 +39,6 @@ const EXCLUDED_TOOL_NAMES = new Set([ "Thinking", ]) -function summarizeInput(input: unknown): string { - if (!input || typeof input !== "object") return "" - const rec = input as Record - const preferredKeys = [ - "command", - "file_path", - "path", - "pattern", - "description", - "url", - "query", - "prompt", - "subagent_type", - ] - for (const key of preferredKeys) { - const v = rec[key] - if (typeof v === "string" && v.length > 0) return v - } - for (const key in rec) { - const v = rec[key] - if (typeof v === "string" && v.length > 0) return v - } - return "" -} - function formatElapsed(ms: number): string { const sec = Math.max(0, Math.floor(ms / 1000)) if (sec < 60) return `${sec}s` @@ -145,7 +121,7 @@ export const TasksWidget = memo(function TasksWidget({ byId.set(part.toolCallId, { toolCallId: part.toolCallId, toolName, - summary: summarizeInput(part.input).slice(0, 80), + summary: summarizeToolInput(part.input).slice(0, 80), startedAt, parentId, children: [],