bingocode 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/desktop/README.md +0 -30
- package/desktop/bunfig.toml +0 -1
- package/desktop/index.html +0 -17
- package/desktop/package.json +0 -55
- package/desktop/pnpm-lock.yaml +0 -3832
- package/desktop/public/app-icon.jpg +0 -0
- package/desktop/public/fonts/inter-latin-ext.woff2 +0 -0
- package/desktop/public/fonts/inter-latin.woff2 +0 -0
- package/desktop/public/fonts/jetbrains-mono-latin-ext.woff2 +0 -0
- package/desktop/public/fonts/jetbrains-mono-latin.woff2 +0 -0
- package/desktop/public/fonts/manrope-latin-ext.woff2 +0 -0
- package/desktop/public/fonts/manrope-latin.woff2 +0 -0
- package/desktop/public/fonts/material-symbols-outlined.woff2 +0 -0
- package/desktop/public/icons/bilibili.svg +0 -1
- package/desktop/public/icons/douyin.svg +0 -1
- package/desktop/public/icons/github.svg +0 -3
- package/desktop/public/icons/xiaohongshu.svg +0 -1
- package/desktop/scripts/build-macos-arm64.sh +0 -270
- package/desktop/scripts/build-sidecars.ts +0 -183
- package/desktop/scripts/build-windows-x64.ps1 +0 -295
- package/desktop/scripts/scan-missing-imports.ts +0 -235
- package/desktop/sidecars/claude-sidecar.ts +0 -156
- package/desktop/src/App.tsx +0 -5
- package/desktop/src/__tests__/agentsSettings.test.tsx +0 -349
- package/desktop/src/__tests__/pages.test.tsx +0 -290
- package/desktop/src/__tests__/skillsSettings.test.tsx +0 -205
- package/desktop/src/api/adapters.ts +0 -12
- package/desktop/src/api/agents.ts +0 -36
- package/desktop/src/api/cliTasks.ts +0 -28
- package/desktop/src/api/client.ts +0 -63
- package/desktop/src/api/computerUse.ts +0 -76
- package/desktop/src/api/filesystem.ts +0 -30
- package/desktop/src/api/hahaOAuth.ts +0 -38
- package/desktop/src/api/models.ts +0 -28
- package/desktop/src/api/providers.ts +0 -63
- package/desktop/src/api/search.ts +0 -29
- package/desktop/src/api/sessions.ts +0 -56
- package/desktop/src/api/settings.ts +0 -20
- package/desktop/src/api/skills.ts +0 -19
- package/desktop/src/api/tasks.ts +0 -36
- package/desktop/src/api/teams.ts +0 -44
- package/desktop/src/api/websocket.ts +0 -164
- package/desktop/src/components/chat/AskUserQuestion.tsx +0 -268
- package/desktop/src/components/chat/AssistantMessage.tsx +0 -29
- package/desktop/src/components/chat/AttachmentGallery.tsx +0 -113
- package/desktop/src/components/chat/ChatInput.tsx +0 -622
- package/desktop/src/components/chat/CodeViewer.tsx +0 -161
- package/desktop/src/components/chat/ComputerUsePermissionModal.test.tsx +0 -174
- package/desktop/src/components/chat/ComputerUsePermissionModal.tsx +0 -311
- package/desktop/src/components/chat/DiffViewer.tsx +0 -157
- package/desktop/src/components/chat/FileSearchMenu.tsx +0 -198
- package/desktop/src/components/chat/ImageGalleryModal.tsx +0 -91
- package/desktop/src/components/chat/InlineImageGallery.tsx +0 -106
- package/desktop/src/components/chat/InlineTaskSummary.tsx +0 -60
- package/desktop/src/components/chat/MermaidRenderer.test.tsx +0 -98
- package/desktop/src/components/chat/MermaidRenderer.tsx +0 -361
- package/desktop/src/components/chat/MessageActionBar.tsx +0 -27
- package/desktop/src/components/chat/MessageList.test.tsx +0 -313
- package/desktop/src/components/chat/MessageList.tsx +0 -249
- package/desktop/src/components/chat/PermissionDialog.tsx +0 -262
- package/desktop/src/components/chat/SessionTaskBar.test.tsx +0 -99
- package/desktop/src/components/chat/SessionTaskBar.tsx +0 -159
- package/desktop/src/components/chat/StreamingIndicator.tsx +0 -41
- package/desktop/src/components/chat/TerminalChrome.tsx +0 -35
- package/desktop/src/components/chat/ThinkingBlock.tsx +0 -87
- package/desktop/src/components/chat/ToolCallBlock.tsx +0 -247
- package/desktop/src/components/chat/ToolCallGroup.tsx +0 -617
- package/desktop/src/components/chat/ToolResultBlock.tsx +0 -107
- package/desktop/src/components/chat/UserMessage.tsx +0 -38
- package/desktop/src/components/chat/chatBlocks.test.tsx +0 -136
- package/desktop/src/components/chat/clipboard.ts +0 -25
- package/desktop/src/components/chat/composerUtils.test.ts +0 -55
- package/desktop/src/components/chat/composerUtils.ts +0 -149
- package/desktop/src/components/controls/ModelSelector.tsx +0 -156
- package/desktop/src/components/controls/PermissionModeSelector.tsx +0 -229
- package/desktop/src/components/layout/AppShell.tsx +0 -107
- package/desktop/src/components/layout/ContentRouter.tsx +0 -27
- package/desktop/src/components/layout/ProjectFilter.tsx +0 -126
- package/desktop/src/components/layout/Sidebar.test.tsx +0 -158
- package/desktop/src/components/layout/Sidebar.tsx +0 -384
- package/desktop/src/components/layout/StatusBar.tsx +0 -31
- package/desktop/src/components/layout/TabBar.test.tsx +0 -136
- package/desktop/src/components/layout/TabBar.tsx +0 -318
- package/desktop/src/components/layout/TitleBar.tsx +0 -96
- package/desktop/src/components/layout/WindowControls.test.tsx +0 -69
- package/desktop/src/components/layout/WindowControls.tsx +0 -89
- package/desktop/src/components/markdown/MarkdownRenderer.test.tsx +0 -100
- package/desktop/src/components/markdown/MarkdownRenderer.tsx +0 -229
- package/desktop/src/components/settings/ClaudeOfficialLogin.tsx +0 -107
- package/desktop/src/components/shared/Button.tsx +0 -63
- package/desktop/src/components/shared/CopyButton.tsx +0 -58
- package/desktop/src/components/shared/DirectoryPicker.tsx +0 -316
- package/desktop/src/components/shared/Dropdown.tsx +0 -91
- package/desktop/src/components/shared/Input.tsx +0 -38
- package/desktop/src/components/shared/Modal.tsx +0 -65
- package/desktop/src/components/shared/ProjectContextChip.tsx +0 -30
- package/desktop/src/components/shared/Spinner.tsx +0 -30
- package/desktop/src/components/shared/Textarea.tsx +0 -38
- package/desktop/src/components/shared/Toast.tsx +0 -47
- package/desktop/src/components/shared/UpdateChecker.tsx +0 -90
- package/desktop/src/components/skills/SkillDetail.test.tsx +0 -89
- package/desktop/src/components/skills/SkillDetail.tsx +0 -403
- package/desktop/src/components/skills/SkillList.tsx +0 -254
- package/desktop/src/components/tasks/DayOfWeekPicker.tsx +0 -57
- package/desktop/src/components/tasks/NewTaskModal.tsx +0 -407
- package/desktop/src/components/tasks/PromptEditor.tsx +0 -74
- package/desktop/src/components/tasks/TaskEmptyState.tsx +0 -30
- package/desktop/src/components/tasks/TaskList.tsx +0 -46
- package/desktop/src/components/tasks/TaskRow.tsx +0 -253
- package/desktop/src/components/tasks/TaskRunsPanel.tsx +0 -195
- package/desktop/src/components/teams/TeamStatusBar.tsx +0 -147
- package/desktop/src/config/providerPresets.ts +0 -78
- package/desktop/src/config/spinnerVerbs.ts +0 -193
- package/desktop/src/hooks/useKeyboardShortcuts.ts +0 -60
- package/desktop/src/i18n/index.ts +0 -54
- package/desktop/src/i18n/locales/en.ts +0 -670
- package/desktop/src/i18n/locales/zh.ts +0 -670
- package/desktop/src/lib/__tests__/cronDescribe.test.ts +0 -93
- package/desktop/src/lib/cronDescribe.ts +0 -188
- package/desktop/src/lib/desktopRuntime.ts +0 -54
- package/desktop/src/lib/parseRunOutput.ts +0 -79
- package/desktop/src/main.tsx +0 -13
- package/desktop/src/mocks/data.ts +0 -202
- package/desktop/src/pages/ActiveSession.test.tsx +0 -181
- package/desktop/src/pages/ActiveSession.tsx +0 -219
- package/desktop/src/pages/AdapterSettings.tsx +0 -375
- package/desktop/src/pages/AgentTeams.tsx +0 -200
- package/desktop/src/pages/ComputerUseSettings.tsx +0 -420
- package/desktop/src/pages/EmptySession.tsx +0 -518
- package/desktop/src/pages/NewTaskModal.tsx +0 -346
- package/desktop/src/pages/ScheduledTasks.tsx +0 -66
- package/desktop/src/pages/ScheduledTasksEmpty.tsx +0 -152
- package/desktop/src/pages/ScheduledTasksList.tsx +0 -416
- package/desktop/src/pages/SessionControls.tsx +0 -460
- package/desktop/src/pages/Settings.tsx +0 -1448
- package/desktop/src/pages/ToolInspection.tsx +0 -235
- package/desktop/src/stores/adapterStore.ts +0 -106
- package/desktop/src/stores/agentStore.ts +0 -34
- package/desktop/src/stores/chatStore.test.ts +0 -505
- package/desktop/src/stores/chatStore.ts +0 -850
- package/desktop/src/stores/cliTaskStore.ts +0 -152
- package/desktop/src/stores/hahaOAuthStore.test.ts +0 -77
- package/desktop/src/stores/hahaOAuthStore.ts +0 -97
- package/desktop/src/stores/providerStore.ts +0 -101
- package/desktop/src/stores/sessionStore.test.ts +0 -63
- package/desktop/src/stores/sessionStore.ts +0 -102
- package/desktop/src/stores/settingsStore.ts +0 -120
- package/desktop/src/stores/skillStore.ts +0 -51
- package/desktop/src/stores/tabStore.ts +0 -169
- package/desktop/src/stores/taskStore.ts +0 -68
- package/desktop/src/stores/teamStore.ts +0 -344
- package/desktop/src/stores/uiStore.ts +0 -100
- package/desktop/src/stores/updateStore.test.ts +0 -71
- package/desktop/src/stores/updateStore.ts +0 -221
- package/desktop/src/theme/globals.css +0 -465
- package/desktop/src/types/adapter.ts +0 -33
- package/desktop/src/types/chat.ts +0 -152
- package/desktop/src/types/cliTask.ts +0 -24
- package/desktop/src/types/provider.ts +0 -62
- package/desktop/src/types/session.ts +0 -27
- package/desktop/src/types/settings.ts +0 -22
- package/desktop/src/types/skill.ts +0 -38
- package/desktop/src/types/task.ts +0 -56
- package/desktop/src/types/team.ts +0 -38
- package/desktop/src-tauri/Cargo.lock +0 -5549
- package/desktop/src-tauri/Cargo.toml +0 -20
- package/desktop/src-tauri/app-icon.svg +0 -13
- package/desktop/src-tauri/build.rs +0 -3
- package/desktop/src-tauri/capabilities/default.json +0 -106
- package/desktop/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml +0 -5
- package/desktop/src-tauri/icons/android/values/ic_launcher_background.xml +0 -4
- package/desktop/src-tauri/icons/icon.icns +0 -0
- package/desktop/src-tauri/icons/icon.ico +0 -0
- package/desktop/src-tauri/src/lib.rs +0 -408
- package/desktop/src-tauri/src/main.rs +0 -6
- package/desktop/src-tauri/tauri.conf.json +0 -78
- package/desktop/src-tauri/tauri.macos.conf.json +0 -18
- package/desktop/src-tauri/tauri.release-ci.json +0 -5
- package/desktop/src-tauri/tauri.windows.conf.json +0 -16
- package/desktop/src-tauri/windows-installer-hooks.nsh +0 -17
- package/desktop/tsconfig.json +0 -25
- package/desktop/vite.config.ts +0 -26
- package/desktop/vitest.config.ts +0 -18
|
@@ -1,617 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react'
|
|
2
|
-
import { ToolCallBlock } from './ToolCallBlock'
|
|
3
|
-
import { MarkdownRenderer } from '../markdown/MarkdownRenderer'
|
|
4
|
-
import { Modal } from '../shared/Modal'
|
|
5
|
-
import { useTranslation } from '../../i18n'
|
|
6
|
-
import type { TranslationKey } from '../../i18n'
|
|
7
|
-
import type { AgentTaskNotification, UIMessage } from '../../types/chat'
|
|
8
|
-
import { AGENT_LIFECYCLE_TYPES } from '../../types/team'
|
|
9
|
-
|
|
10
|
-
type ToolCall = Extract<UIMessage, { type: 'tool_use' }>
|
|
11
|
-
type ToolResult = Extract<UIMessage, { type: 'tool_result' }>
|
|
12
|
-
|
|
13
|
-
type Props = {
|
|
14
|
-
toolCalls: ToolCall[]
|
|
15
|
-
resultMap: Map<string, ToolResult>
|
|
16
|
-
childToolCallsByParent: Map<string, ToolCall[]>
|
|
17
|
-
agentTaskNotifications: Record<string, AgentTaskNotification>
|
|
18
|
-
/** When true, the last tool is still executing — show expanded */
|
|
19
|
-
isStreaming?: boolean
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const TOOL_VERBS: Record<string, (count: number, t: (key: TranslationKey, params?: Record<string, string | number>) => string) => string> = {
|
|
23
|
-
Read: (n, t) => n === 1 ? t('toolGroup.readOne') : t('toolGroup.readMany', { count: n }),
|
|
24
|
-
Write: (n, t) => n === 1 ? t('toolGroup.createdOne') : t('toolGroup.createdMany', { count: n }),
|
|
25
|
-
Edit: (n, t) => n === 1 ? t('toolGroup.editedOne') : t('toolGroup.editedMany', { count: n }),
|
|
26
|
-
Bash: (n, t) => n === 1 ? t('toolGroup.ranOne') : t('toolGroup.ranMany', { count: n }),
|
|
27
|
-
Glob: (_n, t) => t('toolGroup.foundFiles'),
|
|
28
|
-
Grep: (n, t) => n === 1 ? t('toolGroup.searchedOne') : t('toolGroup.searchedMany', { count: n }),
|
|
29
|
-
Agent: (n, t) => n === 1 ? t('toolGroup.agentOne') : t('toolGroup.agentMany', { count: n }),
|
|
30
|
-
WebSearch: (_n, t) => t('toolGroup.searchedWeb'),
|
|
31
|
-
WebFetch: (n, t) => n === 1 ? t('toolGroup.fetchedOne') : t('toolGroup.fetchedMany', { count: n }),
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function generateSummary(toolCalls: ToolCall[], t: (key: TranslationKey, params?: Record<string, string | number>) => string): string {
|
|
35
|
-
const counts = new Map<string, number>()
|
|
36
|
-
for (const tc of toolCalls) {
|
|
37
|
-
counts.set(tc.toolName, (counts.get(tc.toolName) ?? 0) + 1)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const parts: string[] = []
|
|
41
|
-
for (const [name, count] of counts) {
|
|
42
|
-
const verbFn = TOOL_VERBS[name]
|
|
43
|
-
parts.push(verbFn ? verbFn(count, t) : `${name} (${count})`)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return parts.join(', ')
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function groupHasErrors(toolCalls: ToolCall[], resultMap: Map<string, ToolResult>): boolean {
|
|
50
|
-
return toolCalls.some((tc) => {
|
|
51
|
-
const result = resultMap.get(tc.toolUseId)
|
|
52
|
-
return result?.isError
|
|
53
|
-
})
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function ToolCallGroup({
|
|
57
|
-
toolCalls,
|
|
58
|
-
resultMap,
|
|
59
|
-
childToolCallsByParent,
|
|
60
|
-
agentTaskNotifications,
|
|
61
|
-
isStreaming,
|
|
62
|
-
}: Props) {
|
|
63
|
-
const allAgents = toolCalls.every((toolCall) => toolCall.toolName === 'Agent')
|
|
64
|
-
|
|
65
|
-
if (allAgents) {
|
|
66
|
-
return (
|
|
67
|
-
<AgentToolGroup
|
|
68
|
-
toolCalls={toolCalls}
|
|
69
|
-
resultMap={resultMap}
|
|
70
|
-
childToolCallsByParent={childToolCallsByParent}
|
|
71
|
-
agentTaskNotifications={agentTaskNotifications}
|
|
72
|
-
isStreaming={isStreaming}
|
|
73
|
-
/>
|
|
74
|
-
)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Single tool call — render directly without group wrapper
|
|
78
|
-
if (toolCalls.length === 1) {
|
|
79
|
-
const tc = toolCalls[0]!
|
|
80
|
-
return (
|
|
81
|
-
<ToolCallTree
|
|
82
|
-
toolCall={tc}
|
|
83
|
-
resultMap={resultMap}
|
|
84
|
-
childToolCallsByParent={childToolCallsByParent}
|
|
85
|
-
/>
|
|
86
|
-
)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return (
|
|
90
|
-
<ToolCallGroupMulti
|
|
91
|
-
toolCalls={toolCalls}
|
|
92
|
-
resultMap={resultMap}
|
|
93
|
-
childToolCallsByParent={childToolCallsByParent}
|
|
94
|
-
agentTaskNotifications={agentTaskNotifications}
|
|
95
|
-
isStreaming={isStreaming}
|
|
96
|
-
/>
|
|
97
|
-
)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function AgentToolGroup({
|
|
101
|
-
toolCalls,
|
|
102
|
-
resultMap,
|
|
103
|
-
childToolCallsByParent,
|
|
104
|
-
agentTaskNotifications,
|
|
105
|
-
isStreaming,
|
|
106
|
-
}: Props) {
|
|
107
|
-
const [expanded, setExpanded] = useState(true)
|
|
108
|
-
const t = useTranslation()
|
|
109
|
-
const statuses = toolCalls.map((toolCall) =>
|
|
110
|
-
getAgentStatus({
|
|
111
|
-
hasResult: resultMap.has(toolCall.toolUseId),
|
|
112
|
-
isError: !!resultMap.get(toolCall.toolUseId)?.isError,
|
|
113
|
-
isLaunchResult: isAgentLaunchResult(resultMap.get(toolCall.toolUseId)?.content),
|
|
114
|
-
isStreaming: !!isStreaming && !resultMap.has(toolCall.toolUseId),
|
|
115
|
-
childCount: (childToolCallsByParent.get(toolCall.toolUseId) ?? []).length,
|
|
116
|
-
taskStatus: agentTaskNotifications[toolCall.toolUseId]?.status,
|
|
117
|
-
}),
|
|
118
|
-
)
|
|
119
|
-
const isAnyRunning = statuses.some((status) => status === 'running' || status === 'starting')
|
|
120
|
-
const errorPresent = statuses.some((status) => status === 'failed')
|
|
121
|
-
const allComplete = statuses.every((status) => status === 'done')
|
|
122
|
-
const anyStopped = statuses.some((status) => status === 'stopped')
|
|
123
|
-
|
|
124
|
-
useEffect(() => {
|
|
125
|
-
if (isStreaming) {
|
|
126
|
-
setExpanded(true)
|
|
127
|
-
}
|
|
128
|
-
}, [isStreaming])
|
|
129
|
-
|
|
130
|
-
return (
|
|
131
|
-
<div className="mb-2 ml-10">
|
|
132
|
-
<button
|
|
133
|
-
type="button"
|
|
134
|
-
onClick={() => setExpanded((value) => !value)}
|
|
135
|
-
className="flex w-full items-center gap-2 rounded-lg border border-[var(--color-border)]/40 bg-[var(--color-surface-container-low)] px-3 py-1.5 text-left transition-colors hover:bg-[var(--color-surface-container-high)]"
|
|
136
|
-
>
|
|
137
|
-
<span className="material-symbols-outlined text-[14px] text-[var(--color-outline)]">
|
|
138
|
-
{expanded ? 'expand_less' : 'expand_more'}
|
|
139
|
-
</span>
|
|
140
|
-
<span className="flex-1 truncate text-[12px] text-[var(--color-text-secondary)]">
|
|
141
|
-
{toolCalls.length === 1 ? t('toolGroup.agentOne') : t('toolGroup.agentMany', { count: toolCalls.length })}
|
|
142
|
-
</span>
|
|
143
|
-
{isAnyRunning && (
|
|
144
|
-
<span className="rounded-full bg-[var(--color-warning)]/12 px-2 py-0.5 text-[10px] font-semibold text-[var(--color-warning)]">
|
|
145
|
-
{t('agentStatus.running')}
|
|
146
|
-
</span>
|
|
147
|
-
)}
|
|
148
|
-
{!isAnyRunning && errorPresent && (
|
|
149
|
-
<span className="material-symbols-outlined text-[14px] text-[var(--color-error)]">error</span>
|
|
150
|
-
)}
|
|
151
|
-
{!isAnyRunning && !errorPresent && allComplete && (
|
|
152
|
-
<span className="material-symbols-outlined text-[14px] text-[var(--color-success)]">check_circle</span>
|
|
153
|
-
)}
|
|
154
|
-
{!isAnyRunning && !errorPresent && !allComplete && !anyStopped && (
|
|
155
|
-
<span className="material-symbols-outlined text-[14px] text-[var(--color-outline)]">pending</span>
|
|
156
|
-
)}
|
|
157
|
-
{!isAnyRunning && !errorPresent && !allComplete && anyStopped && (
|
|
158
|
-
<span className="material-symbols-outlined text-[14px] text-[var(--color-outline)]">stop_circle</span>
|
|
159
|
-
)}
|
|
160
|
-
</button>
|
|
161
|
-
|
|
162
|
-
{expanded && (
|
|
163
|
-
<div className="relative mt-3 pl-5">
|
|
164
|
-
<div className="absolute bottom-6 left-[11px] top-4 w-px rounded-full bg-[var(--color-border)]/45" />
|
|
165
|
-
<div className="space-y-2">
|
|
166
|
-
{toolCalls.map((toolCall) => (
|
|
167
|
-
<div key={toolCall.id} className="relative pl-7">
|
|
168
|
-
<div className="absolute left-0 top-1/2 -translate-y-1/2">
|
|
169
|
-
<div className="absolute left-[11px] top-1/2 h-px w-4 -translate-y-1/2 bg-[var(--color-border)]/45" />
|
|
170
|
-
<div className="absolute left-[8px] top-1/2 h-2.5 w-2.5 -translate-y-1/2 rounded-full border border-[var(--color-border)]/65 bg-[var(--color-surface-container-lowest)] shadow-[0_0_0_2px_var(--color-surface)]" />
|
|
171
|
-
</div>
|
|
172
|
-
<AgentCallCard
|
|
173
|
-
toolCall={toolCall}
|
|
174
|
-
resultMap={resultMap}
|
|
175
|
-
childToolCallsByParent={childToolCallsByParent}
|
|
176
|
-
agentTaskNotification={agentTaskNotifications[toolCall.toolUseId]}
|
|
177
|
-
isStreaming={isStreaming && !resultMap.has(toolCall.toolUseId)}
|
|
178
|
-
/>
|
|
179
|
-
</div>
|
|
180
|
-
))}
|
|
181
|
-
</div>
|
|
182
|
-
</div>
|
|
183
|
-
)}
|
|
184
|
-
</div>
|
|
185
|
-
)
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/** Separated so the useState hook is never called conditionally. */
|
|
189
|
-
function ToolCallGroupMulti({ toolCalls, resultMap, childToolCallsByParent, isStreaming }: Props) {
|
|
190
|
-
const [expanded, setExpanded] = useState(false)
|
|
191
|
-
const t = useTranslation()
|
|
192
|
-
const summary = generateSummary(toolCalls, t)
|
|
193
|
-
const errorPresent = groupHasErrors(toolCalls, resultMap)
|
|
194
|
-
const allComplete = toolCalls.every((tc) => resultMap.has(tc.toolUseId))
|
|
195
|
-
const hasNestedToolCalls = toolCalls.some((tc) => (childToolCallsByParent.get(tc.toolUseId)?.length ?? 0) > 0)
|
|
196
|
-
|
|
197
|
-
useEffect(() => {
|
|
198
|
-
if (isStreaming || hasNestedToolCalls) {
|
|
199
|
-
setExpanded(true)
|
|
200
|
-
}
|
|
201
|
-
}, [hasNestedToolCalls, isStreaming])
|
|
202
|
-
|
|
203
|
-
return (
|
|
204
|
-
<div className="mb-2 ml-10">
|
|
205
|
-
<button
|
|
206
|
-
type="button"
|
|
207
|
-
onClick={() => setExpanded((v) => !v)}
|
|
208
|
-
className="flex w-full items-center gap-2 rounded-lg border border-[var(--color-border)]/40 bg-[var(--color-surface-container-low)] px-3 py-1.5 text-left transition-colors hover:bg-[var(--color-surface-container-high)]"
|
|
209
|
-
>
|
|
210
|
-
<span className="material-symbols-outlined text-[14px] text-[var(--color-outline)]">
|
|
211
|
-
{expanded ? 'expand_less' : 'expand_more'}
|
|
212
|
-
</span>
|
|
213
|
-
<span className="flex-1 truncate text-[12px] text-[var(--color-text-secondary)]">
|
|
214
|
-
{summary}
|
|
215
|
-
</span>
|
|
216
|
-
{!isStreaming && allComplete && !errorPresent && (
|
|
217
|
-
<span className="material-symbols-outlined text-[14px] text-[var(--color-success)]">check_circle</span>
|
|
218
|
-
)}
|
|
219
|
-
{!isStreaming && errorPresent && (
|
|
220
|
-
<span className="material-symbols-outlined text-[14px] text-[var(--color-error)]">error</span>
|
|
221
|
-
)}
|
|
222
|
-
{!isStreaming && !allComplete && !errorPresent && (
|
|
223
|
-
<span className="material-symbols-outlined text-[14px] text-[var(--color-outline)]">pending</span>
|
|
224
|
-
)}
|
|
225
|
-
{isStreaming && (
|
|
226
|
-
<span className="h-1.5 w-1.5 rounded-full bg-[var(--color-brand)] animate-pulse-dot" />
|
|
227
|
-
)}
|
|
228
|
-
</button>
|
|
229
|
-
|
|
230
|
-
{expanded && (
|
|
231
|
-
<div className="mt-1.5 space-y-1">
|
|
232
|
-
{toolCalls.map((tc) => {
|
|
233
|
-
return (
|
|
234
|
-
<ToolCallTree
|
|
235
|
-
key={tc.id}
|
|
236
|
-
toolCall={tc}
|
|
237
|
-
resultMap={resultMap}
|
|
238
|
-
childToolCallsByParent={childToolCallsByParent}
|
|
239
|
-
compact
|
|
240
|
-
/>
|
|
241
|
-
)
|
|
242
|
-
})}
|
|
243
|
-
</div>
|
|
244
|
-
)}
|
|
245
|
-
</div>
|
|
246
|
-
)
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function AgentCallCard({
|
|
250
|
-
toolCall,
|
|
251
|
-
resultMap,
|
|
252
|
-
childToolCallsByParent,
|
|
253
|
-
agentTaskNotification,
|
|
254
|
-
isStreaming = false,
|
|
255
|
-
}: {
|
|
256
|
-
toolCall: ToolCall
|
|
257
|
-
resultMap: Map<string, ToolResult>
|
|
258
|
-
childToolCallsByParent: Map<string, ToolCall[]>
|
|
259
|
-
agentTaskNotification?: AgentTaskNotification
|
|
260
|
-
isStreaming?: boolean
|
|
261
|
-
}) {
|
|
262
|
-
const [expanded, setExpanded] = useState(false)
|
|
263
|
-
const [previewOpen, setPreviewOpen] = useState(false)
|
|
264
|
-
const t = useTranslation()
|
|
265
|
-
const input = toolCall.input && typeof toolCall.input === 'object'
|
|
266
|
-
? toolCall.input as Record<string, unknown>
|
|
267
|
-
: {}
|
|
268
|
-
const result = resultMap.get(toolCall.toolUseId)
|
|
269
|
-
const childToolCalls = childToolCallsByParent.get(toolCall.toolUseId) ?? []
|
|
270
|
-
const isLaunchResult = isAgentLaunchResult(result?.content)
|
|
271
|
-
const recentToolCalls = childToolCalls.slice(-2)
|
|
272
|
-
const status = getAgentStatus({
|
|
273
|
-
hasResult: !!result,
|
|
274
|
-
isError: !!result?.isError,
|
|
275
|
-
isLaunchResult,
|
|
276
|
-
isStreaming,
|
|
277
|
-
childCount: childToolCalls.length,
|
|
278
|
-
taskStatus: agentTaskNotification?.status,
|
|
279
|
-
})
|
|
280
|
-
const statusClassName = getAgentStatusClassName(status)
|
|
281
|
-
const statusLabel = getAgentStatusLabel(status, t)
|
|
282
|
-
const taskSummary = agentTaskNotification?.summary?.trim() || ''
|
|
283
|
-
const errorText =
|
|
284
|
-
status === 'failed'
|
|
285
|
-
? taskSummary || (result?.isError ? getAgentErrorSummary(result.content) : '')
|
|
286
|
-
: result?.isError
|
|
287
|
-
? getAgentErrorSummary(result.content)
|
|
288
|
-
: ''
|
|
289
|
-
const fullOutputText =
|
|
290
|
-
result && !result.isError && !isLaunchResult && !isAgentLifecycleResult(result.content)
|
|
291
|
-
? extractTextContent(result.content).trim()
|
|
292
|
-
: ''
|
|
293
|
-
const previewText = fullOutputText || (status === 'done' || status === 'stopped' ? taskSummary : '')
|
|
294
|
-
const outputSummary = previewText ? getAgentOutputSummary(previewText) : ''
|
|
295
|
-
const description = typeof input.description === 'string' ? input.description : ''
|
|
296
|
-
|
|
297
|
-
return (
|
|
298
|
-
<div className="overflow-hidden rounded-lg border border-[var(--color-border)]/50 bg-[var(--color-surface-container-lowest)]">
|
|
299
|
-
<div className="flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-[var(--color-surface-hover)]/50">
|
|
300
|
-
<span className="material-symbols-outlined text-[18px] text-[var(--color-outline)]">smart_toy</span>
|
|
301
|
-
<div className="min-w-0 flex-1">
|
|
302
|
-
<div className="flex items-center gap-2">
|
|
303
|
-
<span className="text-[13px] font-semibold text-[var(--color-text-primary)]">Agent</span>
|
|
304
|
-
{description && (
|
|
305
|
-
<span className="truncate text-[12px] text-[var(--color-text-secondary)]">
|
|
306
|
-
{description}
|
|
307
|
-
</span>
|
|
308
|
-
)}
|
|
309
|
-
</div>
|
|
310
|
-
{!expanded && outputSummary && (
|
|
311
|
-
<div className="mt-1 line-clamp-2 text-[11px] text-[var(--color-text-tertiary)]">
|
|
312
|
-
{outputSummary}
|
|
313
|
-
</div>
|
|
314
|
-
)}
|
|
315
|
-
{!expanded && !outputSummary && recentToolCalls.length > 0 && (
|
|
316
|
-
<div className="mt-1 space-y-1">
|
|
317
|
-
{recentToolCalls.map((recentToolCall) => (
|
|
318
|
-
<div
|
|
319
|
-
key={recentToolCall.id}
|
|
320
|
-
className="truncate text-[11px] text-[var(--color-text-tertiary)]"
|
|
321
|
-
>
|
|
322
|
-
{formatRecentToolUseSummary(recentToolCall, resultMap)}
|
|
323
|
-
</div>
|
|
324
|
-
))}
|
|
325
|
-
</div>
|
|
326
|
-
)}
|
|
327
|
-
{!expanded && !outputSummary && !recentToolCalls.length && errorText && (
|
|
328
|
-
<div className="mt-1 truncate text-[11px] text-[var(--color-error)]">
|
|
329
|
-
{errorText}
|
|
330
|
-
</div>
|
|
331
|
-
)}
|
|
332
|
-
</div>
|
|
333
|
-
{outputSummary && (
|
|
334
|
-
<button
|
|
335
|
-
type="button"
|
|
336
|
-
onClick={(event) => {
|
|
337
|
-
event.stopPropagation()
|
|
338
|
-
setPreviewOpen(true)
|
|
339
|
-
}}
|
|
340
|
-
className="shrink-0 rounded-md border border-[var(--color-border)] px-2.5 py-1 text-[11px] font-medium text-[var(--color-text-secondary)] transition-colors hover:bg-[var(--color-surface-hover)] hover:text-[var(--color-text-primary)]"
|
|
341
|
-
>
|
|
342
|
-
{t('agentStatus.viewResult')}
|
|
343
|
-
</button>
|
|
344
|
-
)}
|
|
345
|
-
<span className={`rounded-full px-2 py-0.5 text-[10px] font-semibold ${statusClassName}`}>
|
|
346
|
-
{statusLabel}
|
|
347
|
-
</span>
|
|
348
|
-
<button
|
|
349
|
-
type="button"
|
|
350
|
-
onClick={() => setExpanded((value) => !value)}
|
|
351
|
-
className="flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-[var(--color-outline)] transition-colors hover:bg-[var(--color-surface-hover)]"
|
|
352
|
-
aria-label={expanded ? 'Collapse agent' : 'Expand agent'}
|
|
353
|
-
>
|
|
354
|
-
<span className="material-symbols-outlined text-[16px]">
|
|
355
|
-
{expanded ? 'expand_less' : 'expand_more'}
|
|
356
|
-
</span>
|
|
357
|
-
</button>
|
|
358
|
-
</div>
|
|
359
|
-
|
|
360
|
-
{expanded && (
|
|
361
|
-
<div className="border-t border-[var(--color-border)]/60 px-3 py-3">
|
|
362
|
-
{errorText && (
|
|
363
|
-
<div className="mb-3 rounded-lg border border-[var(--color-error)]/20 bg-[var(--color-error-container)]/60 px-3 py-2 text-[11px] text-[var(--color-error)]">
|
|
364
|
-
{errorText}
|
|
365
|
-
</div>
|
|
366
|
-
)}
|
|
367
|
-
{childToolCalls.length > 0 ? (
|
|
368
|
-
<div className="space-y-1">
|
|
369
|
-
{childToolCalls.map((childToolCall) => (
|
|
370
|
-
<ToolCallTree
|
|
371
|
-
key={childToolCall.id}
|
|
372
|
-
toolCall={childToolCall}
|
|
373
|
-
resultMap={resultMap}
|
|
374
|
-
childToolCallsByParent={childToolCallsByParent}
|
|
375
|
-
compact
|
|
376
|
-
/>
|
|
377
|
-
))}
|
|
378
|
-
</div>
|
|
379
|
-
) : outputSummary ? (
|
|
380
|
-
<div className="rounded-lg border border-[var(--color-border)]/60 bg-[var(--color-surface)] px-3 py-3">
|
|
381
|
-
<div className="line-clamp-3 text-[11px] leading-[1.55] text-[var(--color-text-secondary)]">
|
|
382
|
-
{outputSummary}
|
|
383
|
-
</div>
|
|
384
|
-
<div className="mt-3 flex justify-end">
|
|
385
|
-
<span className="text-[10px] text-[var(--color-text-tertiary)]">
|
|
386
|
-
{t('agentStatus.viewResult')}
|
|
387
|
-
</span>
|
|
388
|
-
</div>
|
|
389
|
-
</div>
|
|
390
|
-
) : (
|
|
391
|
-
<div className="text-[11px] text-[var(--color-text-tertiary)]">
|
|
392
|
-
{status === 'starting' ? t('agentStatus.starting') : t('agentStatus.noActivity')}
|
|
393
|
-
</div>
|
|
394
|
-
)}
|
|
395
|
-
</div>
|
|
396
|
-
)}
|
|
397
|
-
<Modal
|
|
398
|
-
open={previewOpen}
|
|
399
|
-
onClose={() => setPreviewOpen(false)}
|
|
400
|
-
title={description || t('agentStatus.resultTitle')}
|
|
401
|
-
width={900}
|
|
402
|
-
>
|
|
403
|
-
<div className="max-h-[70vh] overflow-y-auto">
|
|
404
|
-
<MarkdownRenderer content={previewText || errorText} />
|
|
405
|
-
</div>
|
|
406
|
-
</Modal>
|
|
407
|
-
</div>
|
|
408
|
-
)
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
function ToolCallTree({
|
|
412
|
-
toolCall,
|
|
413
|
-
resultMap,
|
|
414
|
-
childToolCallsByParent,
|
|
415
|
-
compact = false,
|
|
416
|
-
}: {
|
|
417
|
-
toolCall: ToolCall
|
|
418
|
-
resultMap: Map<string, ToolResult>
|
|
419
|
-
childToolCallsByParent: Map<string, ToolCall[]>
|
|
420
|
-
compact?: boolean
|
|
421
|
-
}) {
|
|
422
|
-
const result = resultMap.get(toolCall.toolUseId)
|
|
423
|
-
const childToolCalls = childToolCallsByParent.get(toolCall.toolUseId) ?? []
|
|
424
|
-
|
|
425
|
-
return (
|
|
426
|
-
<div className={compact ? 'space-y-1' : ''}>
|
|
427
|
-
<ToolCallBlock
|
|
428
|
-
toolName={toolCall.toolName}
|
|
429
|
-
input={toolCall.input}
|
|
430
|
-
result={result ? { content: result.content, isError: result.isError } : null}
|
|
431
|
-
compact={compact}
|
|
432
|
-
/>
|
|
433
|
-
{childToolCalls.length > 0 && (
|
|
434
|
-
<div className={compact ? 'ml-4 border-l border-[var(--color-border)]/60 pl-3' : 'mb-2 ml-16 border-l border-[var(--color-border)]/60 pl-3'}>
|
|
435
|
-
<div className="space-y-1">
|
|
436
|
-
{childToolCalls.map((childToolCall) => (
|
|
437
|
-
<ToolCallTree
|
|
438
|
-
key={childToolCall.id}
|
|
439
|
-
toolCall={childToolCall}
|
|
440
|
-
resultMap={resultMap}
|
|
441
|
-
childToolCallsByParent={childToolCallsByParent}
|
|
442
|
-
compact
|
|
443
|
-
/>
|
|
444
|
-
))}
|
|
445
|
-
</div>
|
|
446
|
-
</div>
|
|
447
|
-
)}
|
|
448
|
-
</div>
|
|
449
|
-
)
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
type AgentStatus = 'starting' | 'running' | 'done' | 'failed' | 'stopped'
|
|
453
|
-
type AgentTaskStatus = AgentTaskNotification['status']
|
|
454
|
-
|
|
455
|
-
function getAgentStatus({
|
|
456
|
-
hasResult,
|
|
457
|
-
isError,
|
|
458
|
-
isLaunchResult,
|
|
459
|
-
isStreaming,
|
|
460
|
-
childCount,
|
|
461
|
-
taskStatus,
|
|
462
|
-
}: {
|
|
463
|
-
hasResult: boolean
|
|
464
|
-
isError: boolean
|
|
465
|
-
isLaunchResult: boolean
|
|
466
|
-
isStreaming: boolean
|
|
467
|
-
childCount: number
|
|
468
|
-
taskStatus?: AgentTaskStatus
|
|
469
|
-
}): AgentStatus {
|
|
470
|
-
if (taskStatus === 'failed') return 'failed'
|
|
471
|
-
if (taskStatus === 'stopped') return 'stopped'
|
|
472
|
-
if (taskStatus === 'completed') return 'done'
|
|
473
|
-
if (hasResult && isError && !isLaunchResult) return 'failed'
|
|
474
|
-
if (hasResult && !isLaunchResult) return 'done'
|
|
475
|
-
if (isStreaming || childCount > 0 || isLaunchResult) return 'running'
|
|
476
|
-
return 'starting'
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
function getAgentStatusLabel(
|
|
480
|
-
status: AgentStatus,
|
|
481
|
-
t: (key: TranslationKey, params?: Record<string, string | number>) => string,
|
|
482
|
-
): string {
|
|
483
|
-
switch (status) {
|
|
484
|
-
case 'failed':
|
|
485
|
-
return t('agentStatus.failed')
|
|
486
|
-
case 'stopped':
|
|
487
|
-
return t('agentStatus.stopped')
|
|
488
|
-
case 'done':
|
|
489
|
-
return t('agentStatus.done')
|
|
490
|
-
case 'running':
|
|
491
|
-
return t('agentStatus.running')
|
|
492
|
-
case 'starting':
|
|
493
|
-
default:
|
|
494
|
-
return t('agentStatus.starting')
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
function getAgentStatusClassName(status: AgentStatus): string {
|
|
499
|
-
switch (status) {
|
|
500
|
-
case 'failed':
|
|
501
|
-
return 'bg-[var(--color-error)]/10 text-[var(--color-error)]'
|
|
502
|
-
case 'stopped':
|
|
503
|
-
return 'bg-[var(--color-surface-container-high)] text-[var(--color-text-secondary)]'
|
|
504
|
-
case 'done':
|
|
505
|
-
return 'bg-[var(--color-success)]/10 text-[var(--color-success)]'
|
|
506
|
-
case 'running':
|
|
507
|
-
return 'bg-[var(--color-warning)]/10 text-[var(--color-warning)]'
|
|
508
|
-
case 'starting':
|
|
509
|
-
default:
|
|
510
|
-
return 'bg-[var(--color-surface-container-high)] text-[var(--color-text-secondary)]'
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
function formatRecentToolUseSummary(
|
|
515
|
-
toolCall: ToolCall,
|
|
516
|
-
resultMap: Map<string, ToolResult>,
|
|
517
|
-
): string {
|
|
518
|
-
const input = toolCall.input && typeof toolCall.input === 'object'
|
|
519
|
-
? toolCall.input as Record<string, unknown>
|
|
520
|
-
: {}
|
|
521
|
-
const result = resultMap.get(toolCall.toolUseId)
|
|
522
|
-
const suffix = result?.isError ? ' • failed' : result ? ' • done' : ' • running'
|
|
523
|
-
|
|
524
|
-
switch (toolCall.toolName) {
|
|
525
|
-
case 'Bash':
|
|
526
|
-
return `Bash · ${typeof input.command === 'string' ? input.command : ''}${suffix}`
|
|
527
|
-
case 'Read':
|
|
528
|
-
return `Read · ${typeof input.file_path === 'string' ? input.file_path.split('/').pop() : 'file'}${suffix}`
|
|
529
|
-
case 'Glob':
|
|
530
|
-
return `Glob · ${typeof input.pattern === 'string' ? input.pattern : ''}${suffix}`
|
|
531
|
-
case 'Grep':
|
|
532
|
-
return `Grep · ${typeof input.pattern === 'string' ? input.pattern : ''}${suffix}`
|
|
533
|
-
case 'Agent':
|
|
534
|
-
return `Agent · ${typeof input.description === 'string' ? input.description : ''}${suffix}`
|
|
535
|
-
default:
|
|
536
|
-
return `${toolCall.toolName}${suffix}`
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
function getAgentErrorSummary(content: unknown): string {
|
|
541
|
-
const text = extractTextContent(content).replace(/\s+/g, ' ').trim()
|
|
542
|
-
if (!text) return ''
|
|
543
|
-
if (text.includes(`Agent type 'Explore' not found`)) {
|
|
544
|
-
return 'Explore agent unavailable in this session'
|
|
545
|
-
}
|
|
546
|
-
return text.length > 120 ? `${text.slice(0, 120)}...` : text
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
function getAgentOutputSummary(content: string): string {
|
|
550
|
-
const text = content.replace(/\s+\n/g, '\n').trim()
|
|
551
|
-
if (!text) return ''
|
|
552
|
-
return text.length > 220 ? `${text.slice(0, 220)}...` : text
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
function isAgentLaunchResult(content: unknown): boolean {
|
|
556
|
-
const text = extractTextContent(content).trim()
|
|
557
|
-
if (!text) return false
|
|
558
|
-
|
|
559
|
-
return (
|
|
560
|
-
text.startsWith('Async agent launched successfully.') ||
|
|
561
|
-
text.startsWith('Remote agent launched in CCR.') ||
|
|
562
|
-
(text.startsWith('Spawned successfully.') &&
|
|
563
|
-
text.includes('The agent is now running and will receive instructions via mailbox.')) ||
|
|
564
|
-
text.includes('The agent is working in the background. You will be notified automatically when it completes.') ||
|
|
565
|
-
text.includes('The agent is running remotely. You will be notified automatically when it completes.')
|
|
566
|
-
)
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
/**
|
|
570
|
-
* Check if agent result content is a lifecycle notification (shutdown, terminated, etc.)
|
|
571
|
-
* rather than actual agent output. These should not be shown to the user as results.
|
|
572
|
-
*/
|
|
573
|
-
function isAgentLifecycleResult(content: unknown): boolean {
|
|
574
|
-
const text = extractTextContent(content).trim()
|
|
575
|
-
if (!text) return false
|
|
576
|
-
// Detect JSON lifecycle messages: shutdown_approved, shutdown_rejected, teammate_terminated
|
|
577
|
-
if (text.startsWith('{') && text.endsWith('}')) {
|
|
578
|
-
try {
|
|
579
|
-
const parsed = JSON.parse(text) as Record<string, unknown>
|
|
580
|
-
if (typeof parsed.type === 'string' && AGENT_LIFECYCLE_TYPES.has(parsed.type)) {
|
|
581
|
-
return true
|
|
582
|
-
}
|
|
583
|
-
} catch {
|
|
584
|
-
// Not valid JSON, not a lifecycle message
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
return false
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
function extractTextContent(content: unknown): string {
|
|
591
|
-
if (typeof content === 'string') return content
|
|
592
|
-
if (Array.isArray(content)) {
|
|
593
|
-
return content
|
|
594
|
-
.map((chunk) => {
|
|
595
|
-
if (typeof chunk === 'string') return chunk
|
|
596
|
-
if (chunk && typeof chunk === 'object' && 'text' in chunk) {
|
|
597
|
-
return typeof chunk.text === 'string' ? chunk.text : ''
|
|
598
|
-
}
|
|
599
|
-
return ''
|
|
600
|
-
})
|
|
601
|
-
.filter(Boolean)
|
|
602
|
-
.join('\n')
|
|
603
|
-
}
|
|
604
|
-
if (content && typeof content === 'object') {
|
|
605
|
-
if (
|
|
606
|
-
'status' in content &&
|
|
607
|
-
(content as Record<string, unknown>).status === 'completed' &&
|
|
608
|
-
Array.isArray((content as Record<string, unknown>).content)
|
|
609
|
-
) {
|
|
610
|
-
return extractTextContent((content as Record<string, unknown>).content)
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
if (content && typeof content === 'object') {
|
|
614
|
-
return JSON.stringify(content)
|
|
615
|
-
}
|
|
616
|
-
return ''
|
|
617
|
-
}
|