Skip to content

Commit 72229a3

Browse files
committed
Many subagents
1 parent b3baa85 commit 72229a3

File tree

19 files changed

+478
-54
lines changed

19 files changed

+478
-54
lines changed

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/queued-messages/queued-messages.tsx

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export function QueuedMessages() {
5454
{/* Message list */}
5555
{isExpanded && (
5656
<div>
57-
{messageQueue.map((msg, index) => (
57+
{messageQueue.map((msg) => (
5858
<div
5959
key={msg.id}
6060
className='group flex items-center gap-2 border-t border-black/[0.04] px-2.5 py-1.5 hover:bg-[var(--bg-tertiary)] dark:border-white/[0.04]'
@@ -71,31 +71,29 @@ export function QueuedMessages() {
7171
</p>
7272
</div>
7373

74-
{/* Actions */}
75-
<div className='flex shrink-0 items-center gap-1'>
76-
{/* Send immediately button */}
74+
{/* Actions - always visible */}
75+
<div className='flex shrink-0 items-center gap-0.5'>
7776
<button
7877
type='button'
7978
onClick={(e) => {
8079
e.stopPropagation()
8180
handleSendNow(msg.id)
8281
}}
83-
className='rounded p-1 text-[var(--text-tertiary)] transition-colors hover:bg-[var(--bg-tertiary)] hover:text-[var(--text-primary)]'
84-
title='Send immediately (stops current)'
82+
className='rounded p-0.5 text-[var(--text-tertiary)] transition-colors hover:bg-[var(--bg-quaternary)] hover:text-[var(--text-primary)]'
83+
title='Send now (aborts current stream)'
8584
>
86-
<ArrowUp className='h-3.5 w-3.5' />
85+
<ArrowUp className='h-3 w-3' />
8786
</button>
88-
{/* Delete button */}
8987
<button
9088
type='button'
9189
onClick={(e) => {
9290
e.stopPropagation()
9391
handleRemove(msg.id)
9492
}}
95-
className='rounded p-1 text-[var(--text-tertiary)] transition-colors hover:bg-[var(--bg-tertiary)] hover:text-red-400'
93+
className='rounded p-0.5 text-[var(--text-tertiary)] transition-colors hover:bg-red-500/10 hover:text-red-400'
9694
title='Remove from queue'
9795
>
98-
<Trash2 className='h-3.5 w-3.5' />
96+
<Trash2 className='h-3 w-3' />
9997
</button>
10098
</div>
10199
</div>

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/tool-call/tool-call.tsx

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -238,11 +238,13 @@ function SubAgentToolCall({ toolCall }: { toolCall: CopilotToolCall }) {
238238
toolCall.state === ClientToolCallState.pending ||
239239
toolCall.state === ClientToolCallState.executing
240240

241+
const showButtons = shouldShowRunSkipButtons(toolCall)
242+
241243
return (
242244
<div className='py-0.5'>
243245
<span className='relative inline-block font-[470] font-season text-[12px] text-[var(--text-tertiary)]'>
244246
{displayName}
245-
{isLoading && (
247+
{isLoading && !showButtons && (
246248
<span
247249
aria-hidden='true'
248250
className='pointer-events-none absolute inset-0 select-none overflow-hidden'
@@ -265,6 +267,7 @@ function SubAgentToolCall({ toolCall }: { toolCall: CopilotToolCall }) {
265267
</span>
266268
)}
267269
</span>
270+
{showButtons && <RunSkipButtons toolCall={toolCall} />}
268271
<style>{`
269272
@keyframes subagent-shimmer {
270273
0% { background-position: 150% 0; }
@@ -303,12 +306,28 @@ const SUBAGENT_SCROLL_INTERVAL = 100
303306
*/
304307
function getSubagentLabels(toolName: string, isStreaming: boolean): string {
305308
switch (toolName) {
306-
case 'debug':
307-
return isStreaming ? 'Debugging' : 'Debugged'
308-
case 'apply_edit':
309-
return isStreaming ? 'Applying edit' : 'Applied edit'
310309
case 'plan':
311310
return isStreaming ? 'Planning' : 'Planned'
311+
case 'edit':
312+
return isStreaming ? 'Editing' : 'Edited'
313+
case 'debug':
314+
return isStreaming ? 'Debugging' : 'Debugged'
315+
case 'test':
316+
return isStreaming ? 'Testing' : 'Tested'
317+
case 'deploy':
318+
return isStreaming ? 'Deploying' : 'Deployed'
319+
case 'auth':
320+
return isStreaming ? 'Authenticating' : 'Authenticated'
321+
case 'research':
322+
return isStreaming ? 'Researching' : 'Researched'
323+
case 'knowledge':
324+
return isStreaming ? 'Managing knowledge' : 'Knowledge managed'
325+
case 'custom_tool':
326+
return isStreaming ? 'Managing custom tool' : 'Custom tool managed'
327+
case 'tour':
328+
return isStreaming ? 'Touring' : 'Tour complete'
329+
case 'info':
330+
return isStreaming ? 'Getting info' : 'Info retrieved'
312331
default:
313332
return isStreaming ? 'Processing' : 'Processed'
314333
}
@@ -786,8 +805,9 @@ export function ToolCall({ toolCall: toolCallProp, toolCallId, onStateChange }:
786805
// Skip rendering some internal tools
787806
if (toolCall.name === 'checkoff_todo' || toolCall.name === 'mark_todo_in_progress') return null
788807

789-
// Special rendering for subagent tools (debug, apply_edit, plan) - only show the collapsible SubAgentContent
790-
const isSubagentTool = toolCall.name === 'debug' || toolCall.name === 'apply_edit' || toolCall.name === 'plan'
808+
// Special rendering for subagent tools - only show the collapsible SubAgentContent
809+
const SUBAGENT_TOOLS = ['plan', 'edit', 'debug', 'test', 'deploy', 'auth', 'research', 'knowledge', 'custom_tool', 'tour', 'info']
810+
const isSubagentTool = SUBAGENT_TOOLS.includes(toolCall.name)
791811
if (isSubagentTool && toolCall.subAgentBlocks && toolCall.subAgentBlocks.length > 0) {
792812
return (
793813
<div className='w-full'>

apps/sim/lib/copilot/registry.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,8 @@ export const ToolArgSchemas = {
206206

207207
manage_custom_tool: z.object({
208208
operation: z
209-
.enum(['add', 'edit', 'delete'])
210-
.describe('The operation to perform: add (create new), edit (update existing), or delete'),
209+
.enum(['add', 'edit', 'delete', 'list'])
210+
.describe('The operation to perform: add (create new), edit (update existing), delete, or list (get all tools)'),
211211
toolId: z
212212
.string()
213213
.optional()
@@ -599,7 +599,7 @@ export const ToolResultSchemas = {
599599
knowledge_base: KnowledgeBaseResultSchema,
600600
manage_custom_tool: z.object({
601601
success: z.boolean(),
602-
operation: z.enum(['add', 'edit', 'delete']),
602+
operation: z.enum(['add', 'edit', 'delete', 'list']),
603603
toolId: z.string().optional(),
604604
title: z.string().optional(),
605605
message: z.string().optional(),

apps/sim/lib/copilot/tools/client/blocks/get-block-options.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,16 @@ export class GetBlockOptionsClientTool extends BaseClientTool {
6363
try {
6464
this.setState(ClientToolCallState.executing)
6565

66-
const { blockId } = GetBlockOptionsInput.parse(args || {})
66+
// Handle both camelCase and snake_case parameter names, plus blockType as an alias
67+
const normalizedArgs = args
68+
? {
69+
blockId: args.blockId || (args as any).block_id || (args as any).blockType || (args as any).block_type,
70+
}
71+
: {}
72+
73+
logger.info('execute called', { originalArgs: args, normalizedArgs })
74+
75+
const { blockId } = GetBlockOptionsInput.parse(normalizedArgs)
6776

6877
const res = await fetch('/api/copilot/execute-copilot-server-tool', {
6978
method: 'POST',
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { KeyRound, Loader2, XCircle } from 'lucide-react'
2+
import {
3+
BaseClientTool,
4+
type BaseClientToolMetadata,
5+
ClientToolCallState,
6+
} from '@/lib/copilot/tools/client/base-tool'
7+
8+
interface AuthArgs {
9+
instruction: string
10+
}
11+
12+
/**
13+
* Auth tool that spawns a subagent to handle authentication setup.
14+
* This tool auto-executes and the actual work is done by the auth subagent.
15+
* The subagent's output is streamed as nested content under this tool call.
16+
*/
17+
export class AuthClientTool extends BaseClientTool {
18+
static readonly id = 'auth'
19+
20+
constructor(toolCallId: string) {
21+
super(toolCallId, AuthClientTool.id, AuthClientTool.metadata)
22+
}
23+
24+
static readonly metadata: BaseClientToolMetadata = {
25+
displayNames: {
26+
[ClientToolCallState.generating]: { text: 'Authenticating', icon: Loader2 },
27+
[ClientToolCallState.pending]: { text: 'Authenticating', icon: Loader2 },
28+
[ClientToolCallState.executing]: { text: 'Authenticating', icon: Loader2 },
29+
[ClientToolCallState.success]: { text: 'Authenticated', icon: KeyRound },
30+
[ClientToolCallState.error]: { text: 'Failed to authenticate', icon: XCircle },
31+
[ClientToolCallState.rejected]: { text: 'Auth skipped', icon: XCircle },
32+
[ClientToolCallState.aborted]: { text: 'Auth aborted', icon: XCircle },
33+
},
34+
}
35+
36+
/**
37+
* Execute the auth tool.
38+
* This just marks the tool as executing - the actual auth work is done server-side
39+
* by the auth subagent, and its output is streamed as subagent events.
40+
*/
41+
async execute(_args?: AuthArgs): Promise<void> {
42+
this.setState(ClientToolCallState.executing)
43+
}
44+
}
45+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { Loader2, Wrench, XCircle } from 'lucide-react'
2+
import {
3+
BaseClientTool,
4+
type BaseClientToolMetadata,
5+
ClientToolCallState,
6+
} from '@/lib/copilot/tools/client/base-tool'
7+
8+
interface CustomToolArgs {
9+
instruction: string
10+
}
11+
12+
/**
13+
* Custom tool that spawns a subagent to manage custom tools.
14+
* This tool auto-executes and the actual work is done by the custom_tool subagent.
15+
* The subagent's output is streamed as nested content under this tool call.
16+
*/
17+
export class CustomToolClientTool extends BaseClientTool {
18+
static readonly id = 'custom_tool'
19+
20+
constructor(toolCallId: string) {
21+
super(toolCallId, CustomToolClientTool.id, CustomToolClientTool.metadata)
22+
}
23+
24+
static readonly metadata: BaseClientToolMetadata = {
25+
displayNames: {
26+
[ClientToolCallState.generating]: { text: 'Managing custom tool', icon: Loader2 },
27+
[ClientToolCallState.pending]: { text: 'Managing custom tool', icon: Loader2 },
28+
[ClientToolCallState.executing]: { text: 'Managing custom tool', icon: Loader2 },
29+
[ClientToolCallState.success]: { text: 'Custom tool managed', icon: Wrench },
30+
[ClientToolCallState.error]: { text: 'Custom tool failed', icon: XCircle },
31+
[ClientToolCallState.rejected]: { text: 'Custom tool skipped', icon: XCircle },
32+
[ClientToolCallState.aborted]: { text: 'Custom tool aborted', icon: XCircle },
33+
},
34+
}
35+
36+
/**
37+
* Execute the custom_tool tool.
38+
* This just marks the tool as executing - the actual custom tool work is done server-side
39+
* by the custom_tool subagent, and its output is streamed as subagent events.
40+
*/
41+
async execute(_args?: CustomToolArgs): Promise<void> {
42+
this.setState(ClientToolCallState.executing)
43+
}
44+
}
45+

apps/sim/lib/copilot/tools/client/other/debug.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export class DebugClientTool extends BaseClientTool {
2424

2525
static readonly metadata: BaseClientToolMetadata = {
2626
displayNames: {
27-
[ClientToolCallState.generating]: { text: 'Preparing debug session', icon: Loader2 },
27+
[ClientToolCallState.generating]: { text: 'Debugging', icon: Loader2 },
2828
[ClientToolCallState.pending]: { text: 'Debugging', icon: Loader2 },
2929
[ClientToolCallState.executing]: { text: 'Debugging', icon: Loader2 },
3030
[ClientToolCallState.success]: { text: 'Debugged', icon: Bug },
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { Loader2, Rocket, XCircle } from 'lucide-react'
2+
import {
3+
BaseClientTool,
4+
type BaseClientToolMetadata,
5+
ClientToolCallState,
6+
} from '@/lib/copilot/tools/client/base-tool'
7+
8+
interface DeployArgs {
9+
instruction: string
10+
}
11+
12+
/**
13+
* Deploy tool that spawns a subagent to handle deployment.
14+
* This tool auto-executes and the actual work is done by the deploy subagent.
15+
* The subagent's output is streamed as nested content under this tool call.
16+
*/
17+
export class DeployClientTool extends BaseClientTool {
18+
static readonly id = 'deploy'
19+
20+
constructor(toolCallId: string) {
21+
super(toolCallId, DeployClientTool.id, DeployClientTool.metadata)
22+
}
23+
24+
static readonly metadata: BaseClientToolMetadata = {
25+
displayNames: {
26+
[ClientToolCallState.generating]: { text: 'Deploying', icon: Loader2 },
27+
[ClientToolCallState.pending]: { text: 'Deploying', icon: Loader2 },
28+
[ClientToolCallState.executing]: { text: 'Deploying', icon: Loader2 },
29+
[ClientToolCallState.success]: { text: 'Deployed', icon: Rocket },
30+
[ClientToolCallState.error]: { text: 'Failed to deploy', icon: XCircle },
31+
[ClientToolCallState.rejected]: { text: 'Deploy skipped', icon: XCircle },
32+
[ClientToolCallState.aborted]: { text: 'Deploy aborted', icon: XCircle },
33+
},
34+
}
35+
36+
/**
37+
* Execute the deploy tool.
38+
* This just marks the tool as executing - the actual deploy work is done server-side
39+
* by the deploy subagent, and its output is streamed as subagent events.
40+
*/
41+
async execute(_args?: DeployArgs): Promise<void> {
42+
this.setState(ClientToolCallState.executing)
43+
}
44+
}
45+

apps/sim/lib/copilot/tools/client/other/apply-edit.ts renamed to apps/sim/lib/copilot/tools/client/other/edit.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,44 +5,44 @@ import {
55
ClientToolCallState,
66
} from '@/lib/copilot/tools/client/base-tool'
77

8-
interface ApplyEditArgs {
8+
interface EditArgs {
99
instruction: string
1010
}
1111

1212
/**
13-
* Apply Edit tool that spawns a subagent to apply code/workflow edits.
14-
* This tool auto-executes and the actual work is done by the apply_edit subagent.
13+
* Edit tool that spawns a subagent to apply code/workflow edits.
14+
* This tool auto-executes and the actual work is done by the edit subagent.
1515
* The subagent's output is streamed as nested content under this tool call.
1616
*/
17-
export class ApplyEditClientTool extends BaseClientTool {
18-
static readonly id = 'apply_edit'
17+
export class EditClientTool extends BaseClientTool {
18+
static readonly id = 'edit'
1919

2020
constructor(toolCallId: string) {
21-
super(toolCallId, ApplyEditClientTool.id, ApplyEditClientTool.metadata)
21+
super(toolCallId, EditClientTool.id, EditClientTool.metadata)
2222
}
2323

2424
static readonly metadata: BaseClientToolMetadata = {
2525
displayNames: {
26-
[ClientToolCallState.generating]: { text: 'Preparing edit', icon: Loader2 },
27-
[ClientToolCallState.pending]: { text: 'Applying edit', icon: Loader2 },
28-
[ClientToolCallState.executing]: { text: 'Applying edit', icon: Loader2 },
29-
[ClientToolCallState.success]: { text: 'Edit applied', icon: Pencil },
26+
[ClientToolCallState.generating]: { text: 'Editing', icon: Loader2 },
27+
[ClientToolCallState.pending]: { text: 'Editing', icon: Loader2 },
28+
[ClientToolCallState.executing]: { text: 'Editing', icon: Loader2 },
29+
[ClientToolCallState.success]: { text: 'Edited', icon: Pencil },
3030
[ClientToolCallState.error]: { text: 'Failed to apply edit', icon: XCircle },
3131
[ClientToolCallState.rejected]: { text: 'Edit skipped', icon: XCircle },
3232
[ClientToolCallState.aborted]: { text: 'Edit aborted', icon: XCircle },
3333
},
3434
}
3535

3636
/**
37-
* Execute the apply_edit tool.
37+
* Execute the edit tool.
3838
* This just marks the tool as executing - the actual edit work is done server-side
39-
* by the apply_edit subagent, and its output is streamed as subagent events.
39+
* by the edit subagent, and its output is streamed as subagent events.
4040
*/
41-
async execute(_args?: ApplyEditArgs): Promise<void> {
41+
async execute(_args?: EditArgs): Promise<void> {
4242
// Immediately transition to executing state - no user confirmation needed
4343
this.setState(ClientToolCallState.executing)
4444
// The tool result will come from the server via tool_result event
45-
// when the apply_edit subagent completes its work
45+
// when the edit subagent completes its work
4646
}
4747
}
4848

0 commit comments

Comments
 (0)