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
8 changes: 4 additions & 4 deletions apps/sim/app/api/copilot/auto-allowed-tools/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { env } from '@/lib/core/config/env'

const logger = createLogger('CopilotAutoAllowedToolsAPI')

/** Headers for server-to-server calls to the Go copilot backend. */
/** Headers for server-to-server calls to the copilot backend. */
function copilotHeaders(): Record<string, string> {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
Expand Down Expand Up @@ -36,7 +36,7 @@ export async function GET() {
)

if (!res.ok) {
logger.warn('Go backend returned error for list auto-allowed', { status: res.status })
logger.warn('Copilot returned error for list auto-allowed', { status: res.status })
return NextResponse.json({ autoAllowedTools: [] })
}

Expand Down Expand Up @@ -73,7 +73,7 @@ export async function POST(request: NextRequest) {
})

if (!res.ok) {
logger.warn('Go backend returned error for add auto-allowed', { status: res.status })
logger.warn('Copilot returned error for add auto-allowed', { status: res.status })
return NextResponse.json({ error: 'Failed to add tool' }, { status: 500 })
}

Expand Down Expand Up @@ -113,7 +113,7 @@ export async function DELETE(request: NextRequest) {
)

if (!res.ok) {
logger.warn('Go backend returned error for remove auto-allowed', { status: res.status })
logger.warn('Copilot returned error for remove auto-allowed', { status: res.status })
return NextResponse.json({ error: 'Failed to remove tool' }, { status: 500 })
}

Expand Down
2 changes: 1 addition & 1 deletion apps/sim/app/api/copilot/chat/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ export async function POST(req: NextRequest) {
const workflowId = resolved.workflowId
const workflowResolvedName = resolved.workflowName

// Resolve workspace from workflow so it can be sent as implicit context to the Go backend.
// Resolve workspace from workflow so it can be sent as implicit context to the copilot.
let resolvedWorkspaceId: string | undefined
try {
const { getWorkflowById } = await import('@/lib/workflows/utils')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
'use client'

import { resolveToolDisplay } from '@/lib/copilot/store-utils'
import { ClientToolCallState } from '@/lib/copilot/tools/client/tool-display-registry'
import type { ContentBlock, OptionItem, SubagentName, ToolCallData } from '../../types'
import { SUBAGENT_LABELS, TOOL_UI_METADATA } from '../../types'
import type { AgentGroupItem } from './components'
import { AgentGroup, ChatContent, CircleStop, Options, PendingTagIndicator } from './components'
import type { AgentGroupItem } from '@/app/workspace/[workspaceId]/home/components/message-content/components'
import {
AgentGroup,
ChatContent,
CircleStop,
Options,
PendingTagIndicator,
} from '@/app/workspace/[workspaceId]/home/components/message-content/components'
import type {
ContentBlock,
MothershipToolName,
OptionItem,
SubagentName,
ToolCallData,
} from '@/app/workspace/[workspaceId]/home/types'
import { SUBAGENT_LABELS, TOOL_UI_METADATA } from '@/app/workspace/[workspaceId]/home/types'

interface TextSegment {
type: 'text'
Expand Down Expand Up @@ -45,16 +55,8 @@ const SUBAGENT_DISPATCH_TOOLS: Record<string, string> = {
file_write: 'workspace_file',
}

function formatToolName(name: string): string {
return name
.replace(/_v\d+$/, '')
.split('_')
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
.join(' ')
}

function resolveAgentLabel(key: string): string {
return SUBAGENT_LABELS[key as SubagentName] ?? formatToolName(key)
return SUBAGENT_LABELS[key as SubagentName] ?? key
}

function isToolDone(status: ToolCallData['status']): boolean {
Expand All @@ -65,41 +67,12 @@ function isDelegatingTool(tc: NonNullable<ContentBlock['toolCall']>): boolean {
return tc.status === 'executing'
}

function mapToolStatusToClientState(
status: ContentBlock['toolCall'] extends { status: infer T } ? T : string
) {
switch (status) {
case 'success':
return ClientToolCallState.success
case 'error':
return ClientToolCallState.error
case 'cancelled':
return ClientToolCallState.cancelled
default:
return ClientToolCallState.executing
}
}

function getOverrideDisplayTitle(tc: NonNullable<ContentBlock['toolCall']>): string | undefined {
if (tc.name === 'read' || tc.name.endsWith('_respond')) {
return resolveToolDisplay(tc.name, mapToolStatusToClientState(tc.status), tc.id, tc.params)
?.text
}
return undefined
}

function toToolData(tc: NonNullable<ContentBlock['toolCall']>): ToolCallData {
const overrideDisplayTitle = getOverrideDisplayTitle(tc)
const displayTitle =
overrideDisplayTitle ||
tc.displayTitle ||
TOOL_UI_METADATA[tc.name as keyof typeof TOOL_UI_METADATA]?.title ||
formatToolName(tc.name)

return {
id: tc.id,
toolName: tc.name,
displayTitle,
displayTitle:
tc.displayTitle ?? TOOL_UI_METADATA[tc.name as MothershipToolName]?.title ?? tc.name,
status: tc.status,
params: tc.params,
result: tc.result,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,34 +23,95 @@ import {
} from '@/components/emcn'
import { Table as TableIcon } from '@/components/emcn/icons'
import { AgentIcon } from '@/components/icons'
import type { MothershipToolName, SubagentName } from '../../types'
import type { MothershipToolName, SubagentName } from '@/app/workspace/[workspaceId]/home/types'

export type IconComponent = ComponentType<SVGProps<SVGSVGElement>>

const TOOL_ICONS: Record<MothershipToolName | SubagentName | 'mothership', IconComponent> = {
mothership: Blimp,
// Workspace
glob: FolderCode,
grep: Search,
read: File,
// Search
search_online: Search,
scrape_page: Search,
get_page_contents: Search,
search_library_docs: Library,
manage_mcp_tool: Settings,
manage_skill: Asterisk,
user_memory: Database,
crawl_website: Search,
// Execution
function_execute: TerminalWindow,
superagent: Blimp,
user_table: TableIcon,
workspace_file: File,
run_workflow: PlayOutline,
run_block: PlayOutline,
run_from_block: PlayOutline,
run_workflow_until_block: PlayOutline,
complete_job: PlayOutline,
get_execution_summary: ClipboardList,
get_job_logs: ClipboardList,
get_workflow_logs: ClipboardList,
get_workflow_data: Layout,
get_block_outputs: ClipboardList,
get_block_upstream_references: ClipboardList,
get_deployed_workflow_state: Rocket,
check_deployment_status: Rocket,
// Workflows & folders
create_workflow: Layout,
delete_workflow: Layout,
edit_workflow: Pencil,
rename_workflow: Pencil,
move_workflow: Layout,
create_folder: FolderCode,
delete_folder: FolderCode,
move_folder: FolderCode,
list_folders: FolderCode,
list_user_workspaces: Layout,
revert_to_version: Rocket,
get_deployment_version: Rocket,
open_resource: Eye,
// Files
workspace_file: File,
download_to_workspace_file: File,
materialize_file: File,
generate_image: File,
generate_visualization: File,
// Tables & knowledge
user_table: TableIcon,
knowledge_base: Database,
// Jobs
create_job: Calendar,
manage_job: Calendar,
update_job_history: Calendar,
// Management
manage_mcp_tool: Settings,
manage_skill: Asterisk,
manage_credential: Integration,
manage_custom_tool: Wrench,
update_workspace_mcp_server: Settings,
delete_workspace_mcp_server: Settings,
create_workspace_mcp_server: Settings,
list_workspace_mcp_servers: Settings,
oauth_get_auth_link: Integration,
oauth_request_access: Integration,
set_environment_variables: Settings,
set_global_workflow_variables: Settings,
get_platform_actions: Settings,
search_documentation: Library,
search_patterns: Search,
deploy_api: Rocket,
deploy_chat: Rocket,
deploy_mcp: Rocket,
redeploy: Rocket,
generate_api_key: Asterisk,
user_memory: Database,
context_write: Pencil,
context_compaction: Asterisk,
// Subagents
build: Hammer,
run: PlayOutline,
deploy: Rocket,
auth: Integration,
knowledge: Database,
knowledge_base: Database,
table: TableIcon,
job: Calendar,
agent: AgentIcon,
Expand All @@ -60,8 +121,6 @@ const TOOL_ICONS: Record<MothershipToolName | SubagentName | 'mothership', IconC
debug: Bug,
edit: Pencil,
fast_edit: Pencil,
context_compaction: Asterisk,
open_resource: Eye,
file_write: File,
}

Expand Down
37 changes: 37 additions & 0 deletions apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,13 @@ function buildReplayStream(events: StreamEventEnvelope[]): ReadableStream<Uint8A
}

function mapStoredBlock(block: TaskStoredContentBlock): ContentBlock {
if (block.type === 'thinking') {
return {
type: 'text',
content: block.content ? `<thinking>${block.content}</thinking>` : '',
}
}

const mapped: ContentBlock = {
type: block.type as ContentBlockType,
content: block.content,
Expand Down Expand Up @@ -1078,6 +1085,36 @@ export function useChat(
}
break
}
case 'reasoning': {
const d = (
parsed.data && typeof parsed.data === 'object' ? parsed.data : {}
) as Record<string, unknown>
const phase = d.phase as string | undefined
if (phase === 'start') {
const tb = ensureTextBlock()
tb.content = `${tb.content ?? ''}<thinking>`
runningText += '<thinking>'
streamingContentRef.current = runningText
flush()
} else if (phase === 'end') {
const tb = ensureTextBlock()
tb.content = `${tb.content ?? ''}</thinking>`
runningText += '</thinking>'
streamingContentRef.current = runningText
flush()
} else {
const chunk =
typeof d.data === 'string' ? d.data : (parsed.content as string | undefined)
if (chunk) {
const tb = ensureTextBlock()
tb.content = (tb.content ?? '') + chunk
runningText += chunk
streamingContentRef.current = runningText
flush()
}
}
break
}
case 'tool_generating':
case 'tool_call': {
const id = parsed.toolCallId
Expand Down
Loading
Loading