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
4 changes: 2 additions & 2 deletions apps/sim/app/api/a2a/serve/[agentId]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ async function handleMessageSend(
headers,
body: JSON.stringify({
...workflowInput,
triggerType: 'api',
triggerType: 'a2a',
...(useInternalAuth && { workflowId: agent.workflowId }),
}),
signal: AbortSignal.timeout(A2A_DEFAULT_TIMEOUT),
Expand Down Expand Up @@ -613,7 +613,7 @@ async function handleMessageStream(
headers,
body: JSON.stringify({
...workflowInput,
triggerType: 'api',
triggerType: 'a2a',
stream: true,
...(useInternalAuth && { workflowId: agent.workflowId }),
}),
Expand Down
18 changes: 5 additions & 13 deletions apps/sim/app/api/workflows/[id]/execute/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { ExecutionSnapshot } from '@/executor/execution/snapshot'
import type { ExecutionMetadata, IterationContext } from '@/executor/execution/types'
import type { StreamingExecution } from '@/executor/types'
import { Serializer } from '@/serializer'
import { CORE_TRIGGER_TYPES } from '@/stores/logs/filters/types'
import { CORE_TRIGGER_TYPES, type CoreTriggerType } from '@/stores/logs/filters/types'

const logger = createLogger('WorkflowExecuteAPI')

Expand Down Expand Up @@ -109,7 +109,7 @@ type AsyncExecutionParams = {
workflowId: string
userId: string
input: any
triggerType: 'api' | 'webhook' | 'schedule' | 'manual' | 'chat' | 'mcp'
triggerType: CoreTriggerType
}

/**
Expand Down Expand Up @@ -253,17 +253,9 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
})

const executionId = uuidv4()
type LoggingTriggerType = 'api' | 'webhook' | 'schedule' | 'manual' | 'chat' | 'mcp'
let loggingTriggerType: LoggingTriggerType = 'manual'
if (
triggerType === 'api' ||
triggerType === 'chat' ||
triggerType === 'webhook' ||
triggerType === 'schedule' ||
triggerType === 'manual' ||
triggerType === 'mcp'
) {
loggingTriggerType = triggerType as LoggingTriggerType
let loggingTriggerType: CoreTriggerType = 'manual'
if (CORE_TRIGGER_TYPES.includes(triggerType as CoreTriggerType)) {
loggingTriggerType = triggerType as CoreTriggerType
}
const loggingSession = new LoggingSession(
workflowId,
Expand Down
1 change: 1 addition & 0 deletions apps/sim/app/workspace/[workspaceId]/logs/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ const TRIGGER_VARIANT_MAP: Record<string, React.ComponentProps<typeof Badge>['va
schedule: 'green',
chat: 'purple',
webhook: 'orange',
a2a: 'teal',
}

interface StatusBadgeProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,8 @@ export function A2aDeploy({
const [skillTags, setSkillTags] = useState<string[]>([])
const [language, setLanguage] = useState<CodeLanguage>('curl')
const [useStreamingExample, setUseStreamingExample] = useState(false)
const [copied, setCopied] = useState(false)
const [urlCopied, setUrlCopied] = useState(false)
const [codeCopied, setCodeCopied] = useState(false)

useEffect(() => {
if (existingAgent) {
Expand Down Expand Up @@ -451,7 +452,7 @@ export function A2aDeploy({
}

try {
if (!isDeployed && onDeployWorkflow) {
if ((!isDeployed || workflowNeedsRedeployment) && onDeployWorkflow) {
await onDeployWorkflow()
}

Expand All @@ -475,6 +476,7 @@ export function A2aDeploy({
}, [
existingAgent,
isDeployed,
workflowNeedsRedeployment,
onDeployWorkflow,
name,
description,
Expand Down Expand Up @@ -643,8 +645,8 @@ console.log(data);`

const handleCopyCommand = useCallback(() => {
navigator.clipboard.writeText(getCurlCommand())
setCopied(true)
setTimeout(() => setCopied(false), 2000)
setCodeCopied(true)
setTimeout(() => setCodeCopied(false), 2000)
}, [getCurlCommand])

if (isLoading) {
Expand Down Expand Up @@ -702,20 +704,20 @@ console.log(data);`
type='button'
onClick={() => {
navigator.clipboard.writeText(endpoint)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
setUrlCopied(true)
setTimeout(() => setUrlCopied(false), 2000)
}}
className='-translate-y-1/2 absolute top-1/2 right-2'
>
{copied ? (
{urlCopied ? (
<Check className='h-3 w-3 text-[var(--brand-tertiary-2)]' />
) : (
<Clipboard className='h-3 w-3 text-[var(--text-tertiary)]' />
)}
</button>
</Tooltip.Trigger>
<Tooltip.Content>
<span>{copied ? 'Copied' : 'Copy'}</span>
<span>{urlCopied ? 'Copied' : 'Copy'}</span>
</Tooltip.Content>
</Tooltip.Root>
</div>
Expand Down Expand Up @@ -871,11 +873,15 @@ console.log(data);`
aria-label='Copy command'
className='!p-1.5 -my-1.5'
>
{copied ? <Check className='h-3 w-3' /> : <Clipboard className='h-3 w-3' />}
{codeCopied ? (
<Check className='h-3 w-3' />
) : (
<Clipboard className='h-3 w-3' />
)}
</Button>
</Tooltip.Trigger>
<Tooltip.Content>
<span>{copied ? 'Copied' : 'Copy'}</span>
<span>{codeCopied ? 'Copied' : 'Copy'}</span>
</Tooltip.Content>
</Tooltip.Root>
</div>
Expand Down
27 changes: 20 additions & 7 deletions apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2319,6 +2319,8 @@ const WorkflowContent = React.memo(() => {
/**
* Handles connection drag end. Detects if the edge was dropped over a block
* and automatically creates a connection to that block's target handle.
* Only creates a connection if ReactFlow didn't already handle it (e.g., when
* dropping on the block body instead of a handle).
*/
const onConnectEnd = useCallback(
(event: MouseEvent | TouchEvent) => {
Expand All @@ -2340,14 +2342,25 @@ const WorkflowContent = React.memo(() => {
// Find node under cursor
const targetNode = findNodeAtPosition(flowPosition)

// Create connection if valid target found
// Create connection if valid target found AND edge doesn't already exist
// ReactFlow's onConnect fires first when dropping on a handle, so we check
// if that connection already exists to avoid creating duplicates.
// IMPORTANT: We must read directly from the store (not React state) because
// the store update from ReactFlow's onConnect may not have triggered a
// React re-render yet when this callback runs (typically 1-2ms later).
if (targetNode && targetNode.id !== source.nodeId) {
onConnect({
source: source.nodeId,
sourceHandle: source.handleId,
target: targetNode.id,
targetHandle: 'target',
})
const currentEdges = useWorkflowStore.getState().edges
const edgeAlreadyExists = currentEdges.some(
(e) => e.source === source.nodeId && e.target === targetNode.id
)
if (!edgeAlreadyExists) {
onConnect({
source: source.nodeId,
sourceHandle: source.handleId,
target: targetNode.id,
targetHandle: 'target',
})
}
}

connectionSourceRef.current = null
Expand Down
3 changes: 2 additions & 1 deletion apps/sim/background/workflow-execution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import { getWorkflowById } from '@/lib/workflows/utils'
import { ExecutionSnapshot } from '@/executor/execution/snapshot'
import type { ExecutionMetadata } from '@/executor/execution/types'
import type { ExecutionResult } from '@/executor/types'
import type { CoreTriggerType } from '@/stores/logs/filters/types'

const logger = createLogger('TriggerWorkflowExecution')

export type WorkflowExecutionPayload = {
workflowId: string
userId: string
input?: any
triggerType?: 'api' | 'webhook' | 'schedule' | 'manual' | 'chat' | 'mcp'
triggerType?: CoreTriggerType
metadata?: Record<string, any>
}

Expand Down
4 changes: 3 additions & 1 deletion apps/sim/components/emcn/components/badge/badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const badgeVariants = cva(
orange: `${STATUS_BASE} bg-[#fed7aa] text-[#c2410c] dark:bg-[rgba(249,115,22,0.2)] dark:text-[#fdba74]`,
amber: `${STATUS_BASE} bg-[#fde68a] text-[#a16207] dark:bg-[rgba(245,158,11,0.2)] dark:text-[#fcd34d]`,
teal: `${STATUS_BASE} bg-[#99f6e4] text-[#0f766e] dark:bg-[rgba(20,184,166,0.2)] dark:text-[#5eead4]`,
cyan: `${STATUS_BASE} bg-[#a5f3fc] text-[#0e7490] dark:bg-[rgba(14,165,233,0.2)] dark:text-[#7dd3fc]`,
'gray-secondary': `${STATUS_BASE} bg-[var(--surface-4)] text-[var(--text-secondary)]`,
},
size: {
Expand All @@ -51,6 +52,7 @@ const STATUS_VARIANTS = [
'orange',
'amber',
'teal',
'cyan',
'gray-secondary',
] as const

Expand Down Expand Up @@ -84,7 +86,7 @@ export interface BadgeProps
* Supports two categories of variants:
* - **Bordered**: `default`, `outline` - traditional badges with borders
* - **Status colors**: `green`, `red`, `gray`, `blue`, `blue-secondary`, `purple`,
* `orange`, `amber`, `teal`, `gray-secondary` - borderless colored badges
* `orange`, `amber`, `teal`, `cyan`, `gray-secondary` - borderless colored badges
*
* Status color variants can display a dot indicator via the `dot` prop.
* All variants support an optional `icon` prop for leading icons.
Expand Down
3 changes: 2 additions & 1 deletion apps/sim/hooks/queries/notifications.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createLogger } from '@sim/logger'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import type { CoreTriggerType } from '@/stores/logs/filters/types'

const logger = createLogger('NotificationQueries')

Expand All @@ -18,7 +19,7 @@ export const notificationKeys = {

type NotificationType = 'webhook' | 'email' | 'slack'
type LogLevel = 'info' | 'error'
type TriggerType = 'api' | 'webhook' | 'schedule' | 'manual' | 'chat' | 'mcp'
type TriggerType = CoreTriggerType

type AlertRuleType =
| 'consecutive_failures'
Expand Down
16 changes: 10 additions & 6 deletions apps/sim/lib/a2a/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,16 @@ export interface A2AFile {
export function extractFileContent(message: Message): A2AFile[] {
return message.parts
.filter((part): part is FilePart => part.kind === 'file')
.map((part) => ({
name: part.file.name,
mimeType: part.file.mimeType,
...('uri' in part.file ? { uri: part.file.uri } : {}),
...('bytes' in part.file ? { bytes: part.file.bytes } : {}),
}))
.map((part) => {
const file = part.file as unknown as Record<string, unknown>
const uri = (file.url as string) || (file.uri as string)
return {
name: file.name as string | undefined,
mimeType: file.mimeType as string | undefined,
...(uri ? { uri } : {}),
...(file.bytes ? { bytes: file.bytes as string } : {}),
}
})
}

export interface ExecutionFileInput {
Expand Down
11 changes: 2 additions & 9 deletions apps/sim/lib/core/rate-limiter/types.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
import { env } from '@/lib/core/config/env'
import type { CoreTriggerType } from '@/stores/logs/filters/types'
import type { TokenBucketConfig } from './storage'

export type TriggerType =
| 'api'
| 'webhook'
| 'schedule'
| 'manual'
| 'chat'
| 'mcp'
| 'form'
| 'api-endpoint'
export type TriggerType = CoreTriggerType | 'form' | 'api-endpoint'

export type RateLimitCounterType = 'sync' | 'async' | 'api-endpoint'

Expand Down
3 changes: 2 additions & 1 deletion apps/sim/lib/execution/preprocessing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { getHighestPrioritySubscription } from '@/lib/billing/core/subscription'
import { RateLimiter } from '@/lib/core/rate-limiter/rate-limiter'
import { LoggingSession } from '@/lib/logs/execution/logging-session'
import { getWorkspaceBilledAccountUserId } from '@/lib/workspaces/utils'
import type { CoreTriggerType } from '@/stores/logs/filters/types'

const logger = createLogger('ExecutionPreprocessing')

Expand Down Expand Up @@ -108,7 +109,7 @@ export interface PreprocessExecutionOptions {
// Required fields
workflowId: string
userId: string // The authenticated user ID
triggerType: 'manual' | 'api' | 'webhook' | 'schedule' | 'chat' | 'mcp' | 'form'
triggerType: CoreTriggerType | 'form'
executionId: string
requestId: string

Expand Down
1 change: 1 addition & 0 deletions apps/sim/lib/logs/get-trigger-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export function getTriggerOptions(): TriggerOption[] {
{ value: 'form', label: 'Form', color: '#06b6d4' },
{ value: 'webhook', label: 'Webhook', color: '#ea580c' },
{ value: 'mcp', label: 'MCP', color: '#dc2626' },
{ value: 'a2a', label: 'A2A', color: '#14b8a6' },
]

for (const trigger of triggers) {
Expand Down
10 changes: 9 additions & 1 deletion apps/sim/stores/logs/filters/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,15 @@ export type TimeRange =

export type LogLevel = 'error' | 'info' | 'running' | 'pending' | 'all' | (string & {})
/** Core trigger types for workflow execution */
export const CORE_TRIGGER_TYPES = ['manual', 'api', 'schedule', 'chat', 'webhook', 'mcp'] as const
export const CORE_TRIGGER_TYPES = [
'manual',
'api',
'schedule',
'chat',
'webhook',
'mcp',
'a2a',
] as const

export type CoreTriggerType = (typeof CORE_TRIGGER_TYPES)[number]

Expand Down