Skip to content

Commit c140e90

Browse files
fix(multi-trigger): resolution paths for triggers (#3002)
* fix(multi-trigger): resolution paths for triggers * fix trigger input format version * fix output condition logic * update type guard: * fix
1 parent d83c418 commit c140e90

File tree

5 files changed

+93
-37
lines changed

5 files changed

+93
-37
lines changed

apps/sim/blocks/blocks/gmail.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,18 @@ export const GmailV2Block: BlockConfig<GmailToolResponse> = {
581581
results: { type: 'json', description: 'Search/read summary results' },
582582
attachments: { type: 'json', description: 'Downloaded attachments (if enabled)' },
583583

584+
// Draft-specific outputs
585+
draftId: {
586+
type: 'string',
587+
description: 'Draft ID',
588+
condition: { field: 'operation', value: 'draft_gmail' },
589+
},
590+
messageId: {
591+
type: 'string',
592+
description: 'Gmail message ID for the draft',
593+
condition: { field: 'operation', value: 'draft_gmail' },
594+
},
595+
584596
// Trigger outputs (unchanged)
585597
email_id: { type: 'string', description: 'Gmail message ID' },
586598
thread_id: { type: 'string', description: 'Gmail thread ID' },

apps/sim/executor/utils/block-data.ts

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,42 @@
1-
import { getBlockOutputs } from '@/lib/workflows/blocks/block-outputs'
21
import { normalizeName } from '@/executor/constants'
32
import type { ExecutionContext } from '@/executor/types'
43
import type { OutputSchema } from '@/executor/utils/block-reference'
4+
import type { SerializedBlock } from '@/serializer/types'
5+
import type { ToolConfig } from '@/tools/types'
6+
import { getTool } from '@/tools/utils'
57

68
export interface BlockDataCollection {
79
blockData: Record<string, unknown>
810
blockNameMapping: Record<string, string>
911
blockOutputSchemas: Record<string, OutputSchema>
1012
}
1113

14+
export function getBlockSchema(
15+
block: SerializedBlock,
16+
toolConfig?: ToolConfig
17+
): OutputSchema | undefined {
18+
const isTrigger =
19+
block.metadata?.category === 'triggers' ||
20+
(block.config?.params as Record<string, unknown> | undefined)?.triggerMode === true
21+
22+
// Triggers use saved outputs (defines the trigger payload schema)
23+
if (isTrigger && block.outputs && Object.keys(block.outputs).length > 0) {
24+
return block.outputs as OutputSchema
25+
}
26+
27+
// When a tool is selected, tool outputs are the source of truth
28+
if (toolConfig?.outputs && Object.keys(toolConfig.outputs).length > 0) {
29+
return toolConfig.outputs as OutputSchema
30+
}
31+
32+
// Fallback to saved outputs for blocks without tools
33+
if (block.outputs && Object.keys(block.outputs).length > 0) {
34+
return block.outputs as OutputSchema
35+
}
36+
37+
return undefined
38+
}
39+
1240
export function collectBlockData(ctx: ExecutionContext): BlockDataCollection {
1341
const blockData: Record<string, unknown> = {}
1442
const blockNameMapping: Record<string, string> = {}
@@ -18,24 +46,21 @@ export function collectBlockData(ctx: ExecutionContext): BlockDataCollection {
1846
if (state.output !== undefined) {
1947
blockData[id] = state.output
2048
}
49+
}
2150

22-
const workflowBlock = ctx.workflow?.blocks?.find((b) => b.id === id)
23-
if (!workflowBlock) continue
51+
const workflowBlocks = ctx.workflow?.blocks ?? []
52+
for (const block of workflowBlocks) {
53+
const id = block.id
2454

25-
if (workflowBlock.metadata?.name) {
26-
blockNameMapping[normalizeName(workflowBlock.metadata.name)] = id
55+
if (block.metadata?.name) {
56+
blockNameMapping[normalizeName(block.metadata.name)] = id
2757
}
2858

29-
const blockType = workflowBlock.metadata?.id
30-
if (blockType) {
31-
const params = workflowBlock.config?.params as Record<string, unknown> | undefined
32-
const subBlocks = params
33-
? Object.fromEntries(Object.entries(params).map(([k, v]) => [k, { value: v }]))
34-
: undefined
35-
const schema = getBlockOutputs(blockType, subBlocks)
36-
if (schema && Object.keys(schema).length > 0) {
37-
blockOutputSchemas[id] = schema
38-
}
59+
const toolId = block.config?.tool
60+
const toolConfig = toolId ? getTool(toolId) : undefined
61+
const schema = getBlockSchema(block, toolConfig)
62+
if (schema && Object.keys(schema).length > 0) {
63+
blockOutputSchemas[id] = schema
3964
}
4065
}
4166

apps/sim/executor/utils/start-block.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -378,8 +378,30 @@ function buildManualTriggerOutput(
378378
return mergeFilesIntoOutput(output, workflowInput)
379379
}
380380

381-
function buildIntegrationTriggerOutput(workflowInput: unknown): NormalizedBlockOutput {
382-
return isPlainObject(workflowInput) ? (workflowInput as NormalizedBlockOutput) : {}
381+
function buildIntegrationTriggerOutput(
382+
workflowInput: unknown,
383+
structuredInput: Record<string, unknown>,
384+
hasStructured: boolean
385+
): NormalizedBlockOutput {
386+
const output: NormalizedBlockOutput = {}
387+
388+
if (hasStructured) {
389+
for (const [key, value] of Object.entries(structuredInput)) {
390+
output[key] = value
391+
}
392+
}
393+
394+
if (isPlainObject(workflowInput)) {
395+
for (const [key, value] of Object.entries(workflowInput)) {
396+
if (value !== undefined && value !== null) {
397+
output[key] = value
398+
} else if (!Object.hasOwn(output, key)) {
399+
output[key] = value
400+
}
401+
}
402+
}
403+
404+
return mergeFilesIntoOutput(output, workflowInput)
383405
}
384406

385407
function extractSubBlocks(block: SerializedBlock): Record<string, unknown> | undefined {
@@ -428,7 +450,7 @@ export function buildStartBlockOutput(options: StartBlockOutputOptions): Normali
428450
return buildManualTriggerOutput(finalInput, workflowInput)
429451

430452
case StartBlockPath.EXTERNAL_TRIGGER:
431-
return buildIntegrationTriggerOutput(workflowInput)
453+
return buildIntegrationTriggerOutput(workflowInput, structuredInput, hasStructured)
432454

433455
case StartBlockPath.LEGACY_STARTER:
434456
return buildLegacyStarterOutput(

apps/sim/executor/variables/resolvers/block.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { getBlockOutputs } from '@/lib/workflows/blocks/block-outputs'
21
import {
32
isReference,
43
normalizeName,
54
parseReferencePath,
65
SPECIAL_REFERENCE_PREFIXES,
76
} from '@/executor/constants'
7+
import { getBlockSchema } from '@/executor/utils/block-data'
88
import {
99
InvalidFieldError,
1010
type OutputSchema,
@@ -67,15 +67,9 @@ export class BlockResolver implements Resolver {
6767
blockData[blockId] = output
6868
}
6969

70-
const blockType = block.metadata?.id
71-
const params = block.config?.params as Record<string, unknown> | undefined
72-
const subBlocks = params
73-
? Object.fromEntries(Object.entries(params).map(([k, v]) => [k, { value: v }]))
74-
: undefined
7570
const toolId = block.config?.tool
7671
const toolConfig = toolId ? getTool(toolId) : undefined
77-
const outputSchema =
78-
toolConfig?.outputs ?? (blockType ? getBlockOutputs(blockType, subBlocks) : block.outputs)
72+
const outputSchema = getBlockSchema(block, toolConfig)
7973

8074
if (outputSchema && Object.keys(outputSchema).length > 0) {
8175
blockOutputSchemas[blockId] = outputSchema

apps/sim/lib/workflows/blocks/block-outputs.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -618,13 +618,6 @@ export function getToolOutputs(
618618
}
619619
}
620620

621-
/**
622-
* Generates output paths for a tool-based block.
623-
*
624-
* @param blockConfig - The block configuration containing tools config
625-
* @param subBlocks - SubBlock values for tool selection and condition evaluation
626-
* @returns Array of output paths for the tool, or empty array on error
627-
*/
628621
export function getToolOutputPaths(
629622
blockConfig: BlockConfig,
630623
subBlocks?: Record<string, SubBlockWithValue>
@@ -634,12 +627,22 @@ export function getToolOutputPaths(
634627
if (!outputs || Object.keys(outputs).length === 0) return []
635628

636629
if (subBlocks && blockConfig.outputs) {
637-
const filteredBlockOutputs = filterOutputsByCondition(blockConfig.outputs, subBlocks)
638-
const allowedKeys = new Set(Object.keys(filteredBlockOutputs))
639-
640630
const filteredOutputs: Record<string, any> = {}
631+
641632
for (const [key, value] of Object.entries(outputs)) {
642-
if (allowedKeys.has(key)) {
633+
const blockOutput = blockConfig.outputs[key]
634+
635+
if (!blockOutput || typeof blockOutput !== 'object') {
636+
filteredOutputs[key] = value
637+
continue
638+
}
639+
640+
const condition = 'condition' in blockOutput ? blockOutput.condition : undefined
641+
if (condition) {
642+
if (evaluateOutputCondition(condition, subBlocks)) {
643+
filteredOutputs[key] = value
644+
}
645+
} else {
643646
filteredOutputs[key] = value
644647
}
645648
}

0 commit comments

Comments
 (0)