Skip to content

Commit 4be4203

Browse files
authored
fix(a2a): removed deployment constraint for redeploying a2a workflows (#2796)
* fix(a2a): removed deployment constraint for redeploying a2a workflows * updated A2A tab copy state * consolidated trigger types const
1 parent b49ed2f commit 4be4203

File tree

13 files changed

+75
-52
lines changed

13 files changed

+75
-52
lines changed

apps/sim/app/api/a2a/serve/[agentId]/route.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ async function handleMessageSend(
384384
headers,
385385
body: JSON.stringify({
386386
...workflowInput,
387-
triggerType: 'api',
387+
triggerType: 'a2a',
388388
...(useInternalAuth && { workflowId: agent.workflowId }),
389389
}),
390390
signal: AbortSignal.timeout(A2A_DEFAULT_TIMEOUT),
@@ -613,7 +613,7 @@ async function handleMessageStream(
613613
headers,
614614
body: JSON.stringify({
615615
...workflowInput,
616-
triggerType: 'api',
616+
triggerType: 'a2a',
617617
stream: true,
618618
...(useInternalAuth && { workflowId: agent.workflowId }),
619619
}),

apps/sim/app/api/workflows/[id]/execute/route.ts

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { ExecutionSnapshot } from '@/executor/execution/snapshot'
2727
import type { ExecutionMetadata, IterationContext } from '@/executor/execution/types'
2828
import type { StreamingExecution } from '@/executor/types'
2929
import { Serializer } from '@/serializer'
30-
import { CORE_TRIGGER_TYPES } from '@/stores/logs/filters/types'
30+
import { CORE_TRIGGER_TYPES, type CoreTriggerType } from '@/stores/logs/filters/types'
3131

3232
const logger = createLogger('WorkflowExecuteAPI')
3333

@@ -109,7 +109,7 @@ type AsyncExecutionParams = {
109109
workflowId: string
110110
userId: string
111111
input: any
112-
triggerType: 'api' | 'webhook' | 'schedule' | 'manual' | 'chat' | 'mcp'
112+
triggerType: CoreTriggerType
113113
}
114114

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

255255
const executionId = uuidv4()
256-
type LoggingTriggerType = 'api' | 'webhook' | 'schedule' | 'manual' | 'chat' | 'mcp'
257-
let loggingTriggerType: LoggingTriggerType = 'manual'
258-
if (
259-
triggerType === 'api' ||
260-
triggerType === 'chat' ||
261-
triggerType === 'webhook' ||
262-
triggerType === 'schedule' ||
263-
triggerType === 'manual' ||
264-
triggerType === 'mcp'
265-
) {
266-
loggingTriggerType = triggerType as LoggingTriggerType
256+
let loggingTriggerType: CoreTriggerType = 'manual'
257+
if (CORE_TRIGGER_TYPES.includes(triggerType as CoreTriggerType)) {
258+
loggingTriggerType = triggerType as CoreTriggerType
267259
}
268260
const loggingSession = new LoggingSession(
269261
workflowId,

apps/sim/app/workspace/[workspaceId]/logs/utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ const TRIGGER_VARIANT_MAP: Record<string, React.ComponentProps<typeof Badge>['va
7272
schedule: 'green',
7373
chat: 'purple',
7474
webhook: 'orange',
75+
a2a: 'teal',
7576
}
7677

7778
interface StatusBadgeProps {

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/a2a/a2a.tsx

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,8 @@ export function A2aDeploy({
204204
const [skillTags, setSkillTags] = useState<string[]>([])
205205
const [language, setLanguage] = useState<CodeLanguage>('curl')
206206
const [useStreamingExample, setUseStreamingExample] = useState(false)
207-
const [copied, setCopied] = useState(false)
207+
const [urlCopied, setUrlCopied] = useState(false)
208+
const [codeCopied, setCodeCopied] = useState(false)
208209

209210
useEffect(() => {
210211
if (existingAgent) {
@@ -451,7 +452,7 @@ export function A2aDeploy({
451452
}
452453

453454
try {
454-
if (!isDeployed && onDeployWorkflow) {
455+
if ((!isDeployed || workflowNeedsRedeployment) && onDeployWorkflow) {
455456
await onDeployWorkflow()
456457
}
457458

@@ -475,6 +476,7 @@ export function A2aDeploy({
475476
}, [
476477
existingAgent,
477478
isDeployed,
479+
workflowNeedsRedeployment,
478480
onDeployWorkflow,
479481
name,
480482
description,
@@ -643,8 +645,8 @@ console.log(data);`
643645

644646
const handleCopyCommand = useCallback(() => {
645647
navigator.clipboard.writeText(getCurlCommand())
646-
setCopied(true)
647-
setTimeout(() => setCopied(false), 2000)
648+
setCodeCopied(true)
649+
setTimeout(() => setCodeCopied(false), 2000)
648650
}, [getCurlCommand])
649651

650652
if (isLoading) {
@@ -702,20 +704,20 @@ console.log(data);`
702704
type='button'
703705
onClick={() => {
704706
navigator.clipboard.writeText(endpoint)
705-
setCopied(true)
706-
setTimeout(() => setCopied(false), 2000)
707+
setUrlCopied(true)
708+
setTimeout(() => setUrlCopied(false), 2000)
707709
}}
708710
className='-translate-y-1/2 absolute top-1/2 right-2'
709711
>
710-
{copied ? (
712+
{urlCopied ? (
711713
<Check className='h-3 w-3 text-[var(--brand-tertiary-2)]' />
712714
) : (
713715
<Clipboard className='h-3 w-3 text-[var(--text-tertiary)]' />
714716
)}
715717
</button>
716718
</Tooltip.Trigger>
717719
<Tooltip.Content>
718-
<span>{copied ? 'Copied' : 'Copy'}</span>
720+
<span>{urlCopied ? 'Copied' : 'Copy'}</span>
719721
</Tooltip.Content>
720722
</Tooltip.Root>
721723
</div>
@@ -871,11 +873,15 @@ console.log(data);`
871873
aria-label='Copy command'
872874
className='!p-1.5 -my-1.5'
873875
>
874-
{copied ? <Check className='h-3 w-3' /> : <Clipboard className='h-3 w-3' />}
876+
{codeCopied ? (
877+
<Check className='h-3 w-3' />
878+
) : (
879+
<Clipboard className='h-3 w-3' />
880+
)}
875881
</Button>
876882
</Tooltip.Trigger>
877883
<Tooltip.Content>
878-
<span>{copied ? 'Copied' : 'Copy'}</span>
884+
<span>{codeCopied ? 'Copied' : 'Copy'}</span>
879885
</Tooltip.Content>
880886
</Tooltip.Root>
881887
</div>

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2319,6 +2319,8 @@ const WorkflowContent = React.memo(() => {
23192319
/**
23202320
* Handles connection drag end. Detects if the edge was dropped over a block
23212321
* and automatically creates a connection to that block's target handle.
2322+
* Only creates a connection if ReactFlow didn't already handle it (e.g., when
2323+
* dropping on the block body instead of a handle).
23222324
*/
23232325
const onConnectEnd = useCallback(
23242326
(event: MouseEvent | TouchEvent) => {
@@ -2340,14 +2342,25 @@ const WorkflowContent = React.memo(() => {
23402342
// Find node under cursor
23412343
const targetNode = findNodeAtPosition(flowPosition)
23422344

2343-
// Create connection if valid target found
2345+
// Create connection if valid target found AND edge doesn't already exist
2346+
// ReactFlow's onConnect fires first when dropping on a handle, so we check
2347+
// if that connection already exists to avoid creating duplicates.
2348+
// IMPORTANT: We must read directly from the store (not React state) because
2349+
// the store update from ReactFlow's onConnect may not have triggered a
2350+
// React re-render yet when this callback runs (typically 1-2ms later).
23442351
if (targetNode && targetNode.id !== source.nodeId) {
2345-
onConnect({
2346-
source: source.nodeId,
2347-
sourceHandle: source.handleId,
2348-
target: targetNode.id,
2349-
targetHandle: 'target',
2350-
})
2352+
const currentEdges = useWorkflowStore.getState().edges
2353+
const edgeAlreadyExists = currentEdges.some(
2354+
(e) => e.source === source.nodeId && e.target === targetNode.id
2355+
)
2356+
if (!edgeAlreadyExists) {
2357+
onConnect({
2358+
source: source.nodeId,
2359+
sourceHandle: source.handleId,
2360+
target: targetNode.id,
2361+
targetHandle: 'target',
2362+
})
2363+
}
23512364
}
23522365

23532366
connectionSourceRef.current = null

apps/sim/background/workflow-execution.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@ import { getWorkflowById } from '@/lib/workflows/utils'
1010
import { ExecutionSnapshot } from '@/executor/execution/snapshot'
1111
import type { ExecutionMetadata } from '@/executor/execution/types'
1212
import type { ExecutionResult } from '@/executor/types'
13+
import type { CoreTriggerType } from '@/stores/logs/filters/types'
1314

1415
const logger = createLogger('TriggerWorkflowExecution')
1516

1617
export type WorkflowExecutionPayload = {
1718
workflowId: string
1819
userId: string
1920
input?: any
20-
triggerType?: 'api' | 'webhook' | 'schedule' | 'manual' | 'chat' | 'mcp'
21+
triggerType?: CoreTriggerType
2122
metadata?: Record<string, any>
2223
}
2324

apps/sim/components/emcn/components/badge/badge.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const badgeVariants = cva(
2525
orange: `${STATUS_BASE} bg-[#fed7aa] text-[#c2410c] dark:bg-[rgba(249,115,22,0.2)] dark:text-[#fdba74]`,
2626
amber: `${STATUS_BASE} bg-[#fde68a] text-[#a16207] dark:bg-[rgba(245,158,11,0.2)] dark:text-[#fcd34d]`,
2727
teal: `${STATUS_BASE} bg-[#99f6e4] text-[#0f766e] dark:bg-[rgba(20,184,166,0.2)] dark:text-[#5eead4]`,
28+
cyan: `${STATUS_BASE} bg-[#a5f3fc] text-[#0e7490] dark:bg-[rgba(14,165,233,0.2)] dark:text-[#7dd3fc]`,
2829
'gray-secondary': `${STATUS_BASE} bg-[var(--surface-4)] text-[var(--text-secondary)]`,
2930
},
3031
size: {
@@ -51,6 +52,7 @@ const STATUS_VARIANTS = [
5152
'orange',
5253
'amber',
5354
'teal',
55+
'cyan',
5456
'gray-secondary',
5557
] as const
5658

@@ -84,7 +86,7 @@ export interface BadgeProps
8486
* Supports two categories of variants:
8587
* - **Bordered**: `default`, `outline` - traditional badges with borders
8688
* - **Status colors**: `green`, `red`, `gray`, `blue`, `blue-secondary`, `purple`,
87-
* `orange`, `amber`, `teal`, `gray-secondary` - borderless colored badges
89+
* `orange`, `amber`, `teal`, `cyan`, `gray-secondary` - borderless colored badges
8890
*
8991
* Status color variants can display a dot indicator via the `dot` prop.
9092
* All variants support an optional `icon` prop for leading icons.

apps/sim/hooks/queries/notifications.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { createLogger } from '@sim/logger'
22
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
3+
import type { CoreTriggerType } from '@/stores/logs/filters/types'
34

45
const logger = createLogger('NotificationQueries')
56

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

1920
type NotificationType = 'webhook' | 'email' | 'slack'
2021
type LogLevel = 'info' | 'error'
21-
type TriggerType = 'api' | 'webhook' | 'schedule' | 'manual' | 'chat' | 'mcp'
22+
type TriggerType = CoreTriggerType
2223

2324
type AlertRuleType =
2425
| 'consecutive_failures'

apps/sim/lib/a2a/utils.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,16 @@ export interface A2AFile {
7878
export function extractFileContent(message: Message): A2AFile[] {
7979
return message.parts
8080
.filter((part): part is FilePart => part.kind === 'file')
81-
.map((part) => ({
82-
name: part.file.name,
83-
mimeType: part.file.mimeType,
84-
...('uri' in part.file ? { uri: part.file.uri } : {}),
85-
...('bytes' in part.file ? { bytes: part.file.bytes } : {}),
86-
}))
81+
.map((part) => {
82+
const file = part.file as unknown as Record<string, unknown>
83+
const uri = (file.url as string) || (file.uri as string)
84+
return {
85+
name: file.name as string | undefined,
86+
mimeType: file.mimeType as string | undefined,
87+
...(uri ? { uri } : {}),
88+
...(file.bytes ? { bytes: file.bytes as string } : {}),
89+
}
90+
})
8791
}
8892

8993
export interface ExecutionFileInput {

apps/sim/lib/core/rate-limiter/types.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
11
import { env } from '@/lib/core/config/env'
2+
import type { CoreTriggerType } from '@/stores/logs/filters/types'
23
import type { TokenBucketConfig } from './storage'
34

4-
export type TriggerType =
5-
| 'api'
6-
| 'webhook'
7-
| 'schedule'
8-
| 'manual'
9-
| 'chat'
10-
| 'mcp'
11-
| 'form'
12-
| 'api-endpoint'
5+
export type TriggerType = CoreTriggerType | 'form' | 'api-endpoint'
136

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

0 commit comments

Comments
 (0)