Checkpoitn

This commit is contained in:
Siddharth Ganesan
2026-02-18 15:29:58 -08:00
parent 7599774974
commit 632e0e0762
9 changed files with 129 additions and 271 deletions

View File

@@ -269,7 +269,7 @@ export const sseHandlers: Record<string, SSEHandler> = {
updatedMap[toolCallId] = {
...current,
state: targetState,
display: resolveToolDisplay(current.name, targetState, current.id, current.params),
display: resolveToolDisplay(current.name, targetState, current.id, current.params, current.serverUI),
}
set({ toolCallsById: updatedMap })
@@ -469,7 +469,8 @@ export const sseHandlers: Record<string, SSEHandler> = {
b.toolCall?.name,
targetState,
toolCallId,
b.toolCall?.params
b.toolCall?.params,
b.toolCall?.serverUI
),
},
}
@@ -507,7 +508,7 @@ export const sseHandlers: Record<string, SSEHandler> = {
updatedMap[toolCallId] = {
...current,
state: targetState,
display: resolveToolDisplay(current.name, targetState, current.id, current.params),
display: resolveToolDisplay(current.name, targetState, current.id, current.params, current.serverUI),
}
set({ toolCallsById: updatedMap })
}
@@ -532,7 +533,8 @@ export const sseHandlers: Record<string, SSEHandler> = {
b.toolCall?.name,
targetState,
toolCallId,
b.toolCall?.params
b.toolCall?.params,
b.toolCall?.serverUI
),
},
}
@@ -579,6 +581,16 @@ export const sseHandlers: Record<string, SSEHandler> = {
const isPartial = toolData.partial === true
const { toolCallsById } = get()
// Extract copilot-provided UI metadata for fallback display
const rawUI = (toolData.ui || data?.ui) as Record<string, unknown> | undefined
const serverUI = rawUI
? {
title: rawUI.title as string | undefined,
phaseLabel: rawUI.phaseLabel as string | undefined,
icon: rawUI.icon as string | undefined,
}
: undefined
const existing = toolCallsById[id]
const toolName = name || existing?.name || 'unknown_tool'
const isAutoAllowed = get().isToolAutoAllowed(toolName)
@@ -592,20 +604,24 @@ export const sseHandlers: Record<string, SSEHandler> = {
initialState = ClientToolCallState.executing
}
const effectiveServerUI = serverUI || existing?.serverUI
const next: CopilotToolCall = existing
? {
...existing,
name: toolName,
state: initialState,
...(args ? { params: args } : {}),
display: resolveToolDisplay(toolName, initialState, id, args || existing.params),
...(effectiveServerUI ? { serverUI: effectiveServerUI } : {}),
display: resolveToolDisplay(toolName, initialState, id, args || existing.params, effectiveServerUI),
}
: {
id,
name: toolName,
state: initialState,
...(args ? { params: args } : {}),
display: resolveToolDisplay(toolName, initialState, id, args),
...(serverUI ? { serverUI } : {}),
display: resolveToolDisplay(toolName, initialState, id, args, serverUI),
}
const updated = { ...toolCallsById, [id]: next }
set({ toolCallsById: updated })

View File

@@ -178,7 +178,7 @@ function setToolState(toolCallId: string, state: ClientToolCallState): void {
[toolCallId]: {
...current,
state,
display: resolveToolDisplay(current.name, state, toolCallId, current.params),
display: resolveToolDisplay(current.name, state, toolCallId, current.params, current.serverUI),
},
}
useCopilotStore.setState({ toolCallsById: updated })

View File

@@ -285,7 +285,7 @@ export const subAgentSSEHandlers: Record<string, SSEHandler> = {
const updatedSubAgentToolCall = {
...existing,
state: targetState,
display: resolveToolDisplay(existing.name, targetState, toolCallId, existing.params),
display: resolveToolDisplay(existing.name, targetState, toolCallId, existing.params, existing.serverUI),
}
context.subAgentToolCalls[parentToolCallId][existingIndex] = updatedSubAgentToolCall

View File

@@ -34,7 +34,7 @@ export function clearStreamingFlags(toolCall: CopilotToolCall): void {
? ClientToolCallState.success
: ClientToolCallState.aborted
toolCall.state = normalized
toolCall.display = resolveToolDisplay(toolCall.name, normalized, toolCall.id, toolCall.params)
toolCall.display = resolveToolDisplay(toolCall.name, normalized, toolCall.id, toolCall.params, toolCall.serverUI)
}
if (Array.isArray(toolCall.subAgentBlocks)) {

View File

@@ -35,6 +35,8 @@ export interface SSEEvent {
phase?: string
/** Set on tool_result events */
failedDependency?: boolean
/** UI metadata from copilot (title, icon, phaseLabel) */
ui?: Record<string, unknown>
}
export type ToolCallStatus = 'pending' | 'executing' | 'success' | 'error' | 'skipped' | 'rejected'

View File

@@ -1,5 +1,28 @@
import { createLogger } from '@sim/logger'
import { Loader2 } from 'lucide-react'
import type { LucideIcon } from 'lucide-react'
import {
BookOpen,
Bug,
Cloud,
Code,
FileText,
Folder,
Globe,
HelpCircle,
Key,
Loader2,
Lock,
Pencil,
Play,
Plus,
Rocket,
Search,
Server,
Settings,
Terminal,
Wrench,
Zap,
} from 'lucide-react'
import {
ClientToolCallState,
type ClientToolDisplay,
@@ -16,16 +39,62 @@ type StoreSet = (
/** Respond tools are internal to copilot subagents and should never be shown in the UI */
const HIDDEN_TOOL_SUFFIX = '_respond'
/** UI metadata sent by the copilot on SSE tool_call events. */
export interface ServerToolUI {
title?: string
phaseLabel?: string
icon?: string
}
/** Maps copilot icon name strings to Lucide icon components. */
const ICON_MAP: Record<string, LucideIcon> = {
search: Search,
globe: Globe,
hammer: Wrench,
rocket: Rocket,
lock: Lock,
book: BookOpen,
wrench: Wrench,
zap: Zap,
play: Play,
cloud: Cloud,
key: Key,
pencil: Pencil,
terminal: Terminal,
workflow: Settings,
settings: Settings,
server: Server,
bug: Bug,
brain: BookOpen,
code: Code,
help: HelpCircle,
plus: Plus,
file: FileText,
folder: Folder,
}
function resolveIcon(iconName: string | undefined): LucideIcon {
if (!iconName) return Loader2
return ICON_MAP[iconName] || Loader2
}
export function resolveToolDisplay(
toolName: string | undefined,
state: ClientToolCallState,
_toolCallId?: string,
params?: Record<string, any>
params?: Record<string, unknown>,
serverUI?: ServerToolUI
): ClientToolDisplay | undefined {
if (!toolName) return undefined
if (toolName.endsWith(HIDDEN_TOOL_SUFFIX)) return undefined
const entry = TOOL_DISPLAY_REGISTRY[toolName]
if (!entry) return humanizedFallback(toolName, state)
if (!entry) {
// Use copilot-provided UI as a better fallback than humanized name
if (serverUI?.title) {
return serverUIFallback(serverUI, state)
}
return humanizedFallback(toolName, state)
}
if (entry.uiConfig?.dynamicText && params) {
const dynamicText = entry.uiConfig.dynamicText(params, state)
@@ -51,6 +120,28 @@ export function resolveToolDisplay(
return humanizedFallback(toolName, state)
}
/** Generates display from copilot-provided UI metadata. */
function serverUIFallback(
serverUI: ServerToolUI,
state: ClientToolCallState
): ClientToolDisplay {
const icon = resolveIcon(serverUI.icon)
const title = serverUI.title!
switch (state) {
case ClientToolCallState.success:
return { text: `Completed ${title.toLowerCase()}`, icon }
case ClientToolCallState.error:
return { text: `Failed ${title.toLowerCase()}`, icon }
case ClientToolCallState.rejected:
return { text: `Skipped ${title.toLowerCase()}`, icon }
case ClientToolCallState.aborted:
return { text: `Aborted ${title.toLowerCase()}`, icon }
default:
return { text: title, icon: Loader2 }
}
}
export function humanizedFallback(
toolName: string,
state: ClientToolCallState
@@ -121,7 +212,7 @@ export function abortAllInProgressTools(set: StoreSet, get: () => CopilotStore)
...tc,
state: resolved,
subAgentStreaming: false,
display: resolveToolDisplay(tc.name, resolved, id, tc.params),
display: resolveToolDisplay(tc.name, resolved, id, tc.params, tc.serverUI),
}
hasUpdates = true
} else if (tc.subAgentStreaming) {
@@ -150,7 +241,7 @@ export function abortAllInProgressTools(set: StoreSet, get: () => CopilotStore)
toolCall: {
...prev,
state: resolved,
display: resolveToolDisplay(prev?.name, resolved, prev?.id, prev?.params),
display: resolveToolDisplay(prev?.name, resolved, prev?.id, prev?.params, prev?.serverUI),
},
}
}

View File

@@ -5,27 +5,20 @@ import {
Check,
CheckCircle,
CheckCircle2,
ClipboardCheck,
Compass,
Database,
FileText,
FlaskConical,
GitBranch,
Globe,
Globe2,
Grid2x2,
Grid2x2Check,
Grid2x2X,
Info,
KeyRound,
ListFilter,
ListTodo,
Loader2,
MessageSquare,
MinusCircle,
Moon,
Navigation,
Pencil,
PencilLine,
Play,
PlugZap,
@@ -334,46 +327,6 @@ const META_build: ToolMetadata = {
},
}
const META_debug: ToolMetadata = {
displayNames: {
[ClientToolCallState.generating]: { text: 'Debugging', icon: Loader2 },
[ClientToolCallState.pending]: { text: 'Debugging', icon: Loader2 },
[ClientToolCallState.executing]: { text: 'Debugging', icon: Loader2 },
[ClientToolCallState.success]: { text: 'Debugged', icon: Bug },
[ClientToolCallState.error]: { text: 'Failed to debug', icon: XCircle },
[ClientToolCallState.rejected]: { text: 'Skipped debug', icon: XCircle },
[ClientToolCallState.aborted]: { text: 'Aborted debug', icon: XCircle },
},
uiConfig: {
subagent: {
streamingLabel: 'Debugging',
completedLabel: 'Debugged',
shouldCollapse: true,
outputArtifacts: [],
},
},
}
const META_discovery: ToolMetadata = {
displayNames: {
[ClientToolCallState.generating]: { text: 'Discovering', icon: Loader2 },
[ClientToolCallState.pending]: { text: 'Discovering', icon: Loader2 },
[ClientToolCallState.executing]: { text: 'Discovering', icon: Loader2 },
[ClientToolCallState.success]: { text: 'Discovered', icon: Search },
[ClientToolCallState.error]: { text: 'Failed to discover', icon: XCircle },
[ClientToolCallState.rejected]: { text: 'Skipped discovery', icon: XCircle },
[ClientToolCallState.aborted]: { text: 'Aborted discovery', icon: XCircle },
},
uiConfig: {
subagent: {
streamingLabel: 'Discovering',
completedLabel: 'Discovered',
shouldCollapse: true,
outputArtifacts: [],
},
},
}
const META_deploy: ToolMetadata = {
displayNames: {
[ClientToolCallState.generating]: { text: 'Deploying', icon: Loader2 },
@@ -564,28 +517,6 @@ const META_deploy_mcp: ToolMetadata = {
},
}
const META_edit: ToolMetadata = {
displayNames: {
[ClientToolCallState.generating]: { text: 'Editing', icon: Loader2 },
[ClientToolCallState.pending]: { text: 'Editing', icon: Loader2 },
[ClientToolCallState.executing]: { text: 'Editing', icon: Loader2 },
[ClientToolCallState.success]: { text: 'Edited', icon: Pencil },
[ClientToolCallState.error]: { text: 'Failed to apply edit', icon: XCircle },
[ClientToolCallState.rejected]: { text: 'Skipped edit', icon: XCircle },
[ClientToolCallState.aborted]: { text: 'Aborted edit', icon: XCircle },
},
uiConfig: {
isSpecial: true,
subagent: {
streamingLabel: 'Editing',
completedLabel: 'Edited',
shouldCollapse: false, // Edit subagent stays expanded
outputArtifacts: ['edit_summary'],
hideThinkingText: true, // We show WorkflowEditSummary instead
},
},
}
const META_edit_workflow: ToolMetadata = {
displayNames: {
[ClientToolCallState.generating]: { text: 'Editing your workflow', icon: Loader2 },
@@ -603,26 +534,6 @@ const META_edit_workflow: ToolMetadata = {
},
}
const META_evaluate: ToolMetadata = {
displayNames: {
[ClientToolCallState.generating]: { text: 'Evaluating', icon: Loader2 },
[ClientToolCallState.pending]: { text: 'Evaluating', icon: Loader2 },
[ClientToolCallState.executing]: { text: 'Evaluating', icon: Loader2 },
[ClientToolCallState.success]: { text: 'Evaluated', icon: ClipboardCheck },
[ClientToolCallState.error]: { text: 'Failed to evaluate', icon: XCircle },
[ClientToolCallState.rejected]: { text: 'Skipped evaluation', icon: XCircle },
[ClientToolCallState.aborted]: { text: 'Aborted evaluation', icon: XCircle },
},
uiConfig: {
subagent: {
streamingLabel: 'Evaluating',
completedLabel: 'Evaluated',
shouldCollapse: true,
outputArtifacts: [],
},
},
}
const META_get_block_outputs: ToolMetadata = {
displayNames: {
[ClientToolCallState.generating]: { text: 'Getting block outputs', icon: Loader2 },
@@ -681,47 +592,6 @@ const META_get_block_upstream_references: ToolMetadata = {
},
}
const META_get_blocks_metadata: ToolMetadata = {
displayNames: {
[ClientToolCallState.generating]: { text: 'Searching block choices', icon: Loader2 },
[ClientToolCallState.pending]: { text: 'Searching block choices', icon: Loader2 },
[ClientToolCallState.executing]: { text: 'Searching block choices', icon: Loader2 },
[ClientToolCallState.success]: { text: 'Searched block choices', icon: ListFilter },
[ClientToolCallState.error]: { text: 'Failed to search block choices', icon: XCircle },
[ClientToolCallState.aborted]: { text: 'Aborted searching block choices', icon: XCircle },
[ClientToolCallState.rejected]: {
text: 'Skipped searching block choices',
icon: MinusCircle,
},
},
getDynamicText: (params, state) => {
if (params?.blockIds && Array.isArray(params.blockIds) && params.blockIds.length > 0) {
const blockList = params.blockIds
.slice(0, 3)
.map((blockId) => blockId.replace(/_/g, ' '))
.join(', ')
const more = params.blockIds.length > 3 ? '...' : ''
const blocks = `${blockList}${more}`
switch (state) {
case ClientToolCallState.success:
return `Searched ${blocks}`
case ClientToolCallState.executing:
case ClientToolCallState.generating:
case ClientToolCallState.pending:
return `Searching ${blocks}`
case ClientToolCallState.error:
return `Failed to search ${blocks}`
case ClientToolCallState.aborted:
return `Aborted searching ${blocks}`
case ClientToolCallState.rejected:
return `Skipped searching ${blocks}`
}
}
return undefined
},
}
const META_get_examples_rag: ToolMetadata = {
displayNames: {
[ClientToolCallState.generating]: { text: 'Fetching examples', icon: Loader2 },
@@ -843,19 +713,6 @@ const META_get_page_contents: ToolMetadata = {
},
}
const META_get_trigger_blocks: ToolMetadata = {
displayNames: {
[ClientToolCallState.generating]: { text: 'Finding trigger blocks', icon: Loader2 },
[ClientToolCallState.pending]: { text: 'Finding trigger blocks', icon: Loader2 },
[ClientToolCallState.executing]: { text: 'Finding trigger blocks', icon: Loader2 },
[ClientToolCallState.success]: { text: 'Found trigger blocks', icon: ListFilter },
[ClientToolCallState.error]: { text: 'Failed to find trigger blocks', icon: XCircle },
[ClientToolCallState.aborted]: { text: 'Aborted finding trigger blocks', icon: MinusCircle },
[ClientToolCallState.rejected]: { text: 'Skipped finding trigger blocks', icon: MinusCircle },
},
interrupt: undefined,
}
const META_get_trigger_examples: ToolMetadata = {
displayNames: {
[ClientToolCallState.generating]: { text: 'Selecting a trigger', icon: Loader2 },
@@ -951,26 +808,6 @@ const META_get_workflow_data: ToolMetadata = {
},
}
const META_info: ToolMetadata = {
displayNames: {
[ClientToolCallState.generating]: { text: 'Getting info', icon: Loader2 },
[ClientToolCallState.pending]: { text: 'Getting info', icon: Loader2 },
[ClientToolCallState.executing]: { text: 'Getting info', icon: Loader2 },
[ClientToolCallState.success]: { text: 'Retrieved info', icon: Info },
[ClientToolCallState.error]: { text: 'Failed to get info', icon: XCircle },
[ClientToolCallState.rejected]: { text: 'Skipped info', icon: XCircle },
[ClientToolCallState.aborted]: { text: 'Aborted info', icon: XCircle },
},
uiConfig: {
subagent: {
streamingLabel: 'Getting info',
completedLabel: 'Info retrieved',
shouldCollapse: true,
outputArtifacts: [],
},
},
}
const META_knowledge: ToolMetadata = {
displayNames: {
[ClientToolCallState.generating]: { text: 'Managing knowledge', icon: Loader2 },
@@ -1404,26 +1241,6 @@ const META_oauth_request_access: ToolMetadata = {
},
}
const META_plan: ToolMetadata = {
displayNames: {
[ClientToolCallState.generating]: { text: 'Planning', icon: Loader2 },
[ClientToolCallState.pending]: { text: 'Planning', icon: Loader2 },
[ClientToolCallState.executing]: { text: 'Planning', icon: Loader2 },
[ClientToolCallState.success]: { text: 'Planned', icon: ListTodo },
[ClientToolCallState.error]: { text: 'Failed to plan', icon: XCircle },
[ClientToolCallState.rejected]: { text: 'Skipped plan', icon: XCircle },
[ClientToolCallState.aborted]: { text: 'Aborted plan', icon: XCircle },
},
uiConfig: {
subagent: {
streamingLabel: 'Planning',
completedLabel: 'Planned',
shouldCollapse: true,
outputArtifacts: ['plan'],
},
},
}
const META_redeploy: ToolMetadata = {
displayNames: {
[ClientToolCallState.generating]: { text: 'Redeploying workflow', icon: Loader2 },
@@ -2266,66 +2083,6 @@ const META_superagent: ToolMetadata = {
},
}
const META_test: ToolMetadata = {
displayNames: {
[ClientToolCallState.generating]: { text: 'Testing', icon: Loader2 },
[ClientToolCallState.pending]: { text: 'Testing', icon: Loader2 },
[ClientToolCallState.executing]: { text: 'Testing', icon: Loader2 },
[ClientToolCallState.success]: { text: 'Tested', icon: FlaskConical },
[ClientToolCallState.error]: { text: 'Failed to test', icon: XCircle },
[ClientToolCallState.rejected]: { text: 'Skipped test', icon: XCircle },
[ClientToolCallState.aborted]: { text: 'Aborted test', icon: XCircle },
},
uiConfig: {
subagent: {
streamingLabel: 'Testing',
completedLabel: 'Tested',
shouldCollapse: true,
outputArtifacts: [],
},
},
}
const META_tour: ToolMetadata = {
displayNames: {
[ClientToolCallState.generating]: { text: 'Touring', icon: Loader2 },
[ClientToolCallState.pending]: { text: 'Touring', icon: Loader2 },
[ClientToolCallState.executing]: { text: 'Touring', icon: Loader2 },
[ClientToolCallState.success]: { text: 'Completed tour', icon: Compass },
[ClientToolCallState.error]: { text: 'Failed tour', icon: XCircle },
[ClientToolCallState.rejected]: { text: 'Skipped tour', icon: XCircle },
[ClientToolCallState.aborted]: { text: 'Aborted tour', icon: XCircle },
},
uiConfig: {
subagent: {
streamingLabel: 'Touring',
completedLabel: 'Tour complete',
shouldCollapse: true,
outputArtifacts: [],
},
},
}
const META_workflow: ToolMetadata = {
displayNames: {
[ClientToolCallState.generating]: { text: 'Managing workflow', icon: Loader2 },
[ClientToolCallState.pending]: { text: 'Managing workflow', icon: Loader2 },
[ClientToolCallState.executing]: { text: 'Managing workflow', icon: Loader2 },
[ClientToolCallState.success]: { text: 'Managed workflow', icon: GitBranch },
[ClientToolCallState.error]: { text: 'Failed to manage workflow', icon: XCircle },
[ClientToolCallState.rejected]: { text: 'Skipped workflow', icon: XCircle },
[ClientToolCallState.aborted]: { text: 'Aborted workflow', icon: XCircle },
},
uiConfig: {
subagent: {
streamingLabel: 'Managing workflow',
completedLabel: 'Workflow managed',
shouldCollapse: true,
outputArtifacts: [],
},
},
}
const TOOL_METADATA_BY_ID: Record<string, ToolMetadata> = {
auth: META_auth,
check_deployment_status: META_check_deployment_status,
@@ -2334,28 +2091,21 @@ const TOOL_METADATA_BY_ID: Record<string, ToolMetadata> = {
create_workspace_mcp_server: META_create_workspace_mcp_server,
build: META_build,
custom_tool: META_custom_tool,
debug: META_debug,
deploy: META_deploy,
discovery: META_discovery,
deploy_api: META_deploy_api,
deploy_chat: META_deploy_chat,
deploy_mcp: META_deploy_mcp,
edit: META_edit,
edit_workflow: META_edit_workflow,
evaluate: META_evaluate,
get_block_outputs: META_get_block_outputs,
get_block_upstream_references: META_get_block_upstream_references,
get_blocks_metadata: META_get_blocks_metadata,
generate_api_key: META_generate_api_key,
get_examples_rag: META_get_examples_rag,
get_operations_examples: META_get_operations_examples,
get_page_contents: META_get_page_contents,
get_platform_actions: META_get_platform_actions,
get_trigger_blocks: META_get_trigger_blocks,
get_trigger_examples: META_get_trigger_examples,
get_workflow_console: META_get_workflow_console,
get_workflow_data: META_get_workflow_data,
info: META_info,
knowledge: META_knowledge,
knowledge_base: META_knowledge_base,
list_workspace_mcp_servers: META_list_workspace_mcp_servers,
@@ -2365,7 +2115,6 @@ const TOOL_METADATA_BY_ID: Record<string, ToolMetadata> = {
mark_todo_in_progress: META_mark_todo_in_progress,
navigate_ui: META_navigate_ui,
oauth_request_access: META_oauth_request_access,
plan: META_plan,
redeploy: META_redeploy,
remember_debug: META_remember_debug,
research: META_research,
@@ -2384,9 +2133,6 @@ const TOOL_METADATA_BY_ID: Record<string, ToolMetadata> = {
sleep: META_sleep,
summarize_conversation: META_summarize_conversation,
superagent: META_superagent,
test: META_test,
tour: META_tour,
workflow: META_workflow,
}
export const TOOL_DISPLAY_REGISTRY: Record<string, ToolDisplayEntry> = Object.fromEntries(

View File

@@ -1650,7 +1650,7 @@ export const useCopilotStore = create<CopilotStore>()(
map[id] = {
...current,
state: norm,
display: resolveToolDisplay(current.name, norm, id, current.params),
display: resolveToolDisplay(current.name, norm, id, current.params, current.serverUI),
}
set({ toolCallsById: map })
} catch (error) {
@@ -1671,7 +1671,7 @@ export const useCopilotStore = create<CopilotStore>()(
map[toolCallId] = {
...current,
params: updatedParams,
display: resolveToolDisplay(current.name, current.state, toolCallId, updatedParams),
display: resolveToolDisplay(current.name, current.state, toolCallId, updatedParams, current.serverUI),
}
set({ toolCallsById: map })
} catch (error) {
@@ -1728,7 +1728,7 @@ export const useCopilotStore = create<CopilotStore>()(
// Update store map
const updatedMap = { ...toolCallsById }
const updatedDisplay = resolveToolDisplay(current.name, targetState, id, current.params)
const updatedDisplay = resolveToolDisplay(current.name, targetState, id, current.params, current.serverUI)
updatedMap[id] = {
...current,
state: targetState,

View File

@@ -4,6 +4,7 @@ import type { AvailableModel } from '@/lib/copilot/types'
export type { CopilotMode, CopilotModelId } from '@/lib/copilot/models'
import type { ClientContentBlock } from '@/lib/copilot/client-sse/types'
import type { ServerToolUI } from '@/lib/copilot/store-utils'
import type { ClientToolCallState, ClientToolDisplay } from '@/lib/copilot/tools/client/base-tool'
import type { WorkflowState } from '@/stores/workflows/workflow/types'
@@ -26,6 +27,8 @@ export interface CopilotToolCall {
params?: Record<string, unknown>
input?: Record<string, unknown>
display?: ClientToolDisplay
/** UI metadata from the copilot SSE event (used as fallback for unregistered tools) */
serverUI?: ServerToolUI
/** Content streamed from a subagent (e.g., debug agent) */
subAgentContent?: string
/** Tool calls made by the subagent */