Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down
26 changes: 21 additions & 5 deletions src/renderer/features/agents/main/assistant-message-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -518,7 +522,7 @@ export const AssistantMessageItem = memo(function AssistantMessageItem({
const nestedToolIds = new Set<string>()
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<string, { parts: any[]; firstToolCallId: string }>()
Expand Down Expand Up @@ -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 <AgentTaskTool key={idx} part={part} nestedTools={nestedTools} chatStatus={status} />
}
Expand Down Expand Up @@ -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 (
<div key={idx} className="text-xs text-muted-foreground py-0.5 px-2">
{part.type.replace("tool-", "")}
<div
key={idx}
className="text-xs py-0.5 px-2 flex items-center gap-1.5 min-w-0"
>
<span className="font-medium text-foreground flex-shrink-0">
{toolName}
</span>
{summary && (
<span className="text-muted-foreground truncate min-w-0">
{summary}
</span>
)}
</div>
)
}
Expand Down
41 changes: 41 additions & 0 deletions src/renderer/features/agents/ui/agent-tool-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, unknown>
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/
Expand Down
28 changes: 2 additions & 26 deletions src/renderer/features/details-sidebar/sections/tasks-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<string, unknown>
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`
Expand Down Expand Up @@ -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: [],
Expand Down