Skip to content

Commit 2731d98

Browse files
committed
fix(hitl): add missing fields to block configs
1 parent 46ba315 commit 2731d98

File tree

14 files changed

+153
-143
lines changed

14 files changed

+153
-143
lines changed

apps/sim/app/resume/[workflowId]/[executionId]/resume-page-client.tsx

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { useRouter } from 'next/navigation'
66
import {
77
Badge,
88
Button,
9-
Code,
109
Input,
1110
Label,
1211
Table,
@@ -777,15 +776,6 @@ export default function ResumeExecutionPage({
777776
refreshSelectedDetail,
778777
])
779778

780-
const pauseResponsePreview = useMemo(() => {
781-
if (!selectedDetail?.pausePoint.response?.data) return '{}'
782-
try {
783-
return JSON.stringify(selectedDetail.pausePoint.response.data, null, 2)
784-
} catch {
785-
return String(selectedDetail.pausePoint.response.data)
786-
}
787-
}, [selectedDetail])
788-
789779
const isFormComplete = useMemo(() => {
790780
if (!isHumanMode || !hasInputFormat) return true
791781
return inputFormatFields.every((field) => {
@@ -1155,10 +1145,12 @@ export default function ResumeExecutionPage({
11551145
borderBottom: '1px solid var(--border)',
11561146
}}
11571147
>
1158-
<Label>Pause Data</Label>
1148+
<Label>Display Data</Label>
11591149
</div>
11601150
<div style={{ padding: '16px' }}>
1161-
<Code.Viewer code={pauseResponsePreview} language='json' />
1151+
<p style={{ fontSize: '13px', color: 'var(--text-muted)' }}>
1152+
No display data configured
1153+
</p>
11621154
</div>
11631155
</div>
11641156
)}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown.tsx

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,19 +1183,6 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
11831183
const outputPaths = getBlockOutputPaths(sourceBlock.type, mergedSubBlocks, true)
11841184
blockTags = outputPaths.map((path) => `${normalizedBlockName}.${path}`)
11851185
}
1186-
} else if (sourceBlock.type === 'approval') {
1187-
const dynamicOutputs = getBlockOutputPaths(sourceBlock.type, mergedSubBlocks)
1188-
1189-
const isSelfReference = activeSourceBlockId === blockId
1190-
1191-
if (dynamicOutputs.length > 0) {
1192-
const allTags = dynamicOutputs.map((path) => `${normalizedBlockName}.${path}`)
1193-
blockTags = isSelfReference ? allTags.filter((tag) => tag.endsWith('.url')) : allTags
1194-
} else {
1195-
const outputPaths = getBlockOutputPaths(sourceBlock.type, mergedSubBlocks)
1196-
const allTags = outputPaths.map((path) => `${normalizedBlockName}.${path}`)
1197-
blockTags = isSelfReference ? allTags.filter((tag) => tag.endsWith('.url')) : allTags
1198-
}
11991186
} else if (sourceBlock.type === 'human_in_the_loop') {
12001187
const dynamicOutputs = getBlockOutputPaths(sourceBlock.type, mergedSubBlocks)
12011188

@@ -1400,13 +1387,8 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
14001387
if (!accessibleBlock) continue
14011388

14021389
// Skip the current block - blocks cannot reference their own outputs
1403-
// Exception: approval and human_in_the_loop blocks can reference their own outputs
1404-
if (
1405-
accessibleBlockId === blockId &&
1406-
accessibleBlock.type !== 'approval' &&
1407-
accessibleBlock.type !== 'human_in_the_loop'
1408-
)
1409-
continue
1390+
// Exception: human_in_the_loop blocks can reference their own outputs (url, resumeEndpoint)
1391+
if (accessibleBlockId === blockId && accessibleBlock.type !== 'human_in_the_loop') continue
14101392

14111393
const blockConfig = getBlock(accessibleBlock.type)
14121394

@@ -1520,19 +1502,6 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
15201502
const outputPaths = getBlockOutputPaths(accessibleBlock.type, mergedSubBlocks, true)
15211503
blockTags = outputPaths.map((path) => `${normalizedBlockName}.${path}`)
15221504
}
1523-
} else if (accessibleBlock.type === 'approval') {
1524-
const dynamicOutputs = getBlockOutputPaths(accessibleBlock.type, mergedSubBlocks)
1525-
1526-
const isSelfReference = accessibleBlockId === blockId
1527-
1528-
if (dynamicOutputs.length > 0) {
1529-
const allTags = dynamicOutputs.map((path) => `${normalizedBlockName}.${path}`)
1530-
blockTags = isSelfReference ? allTags.filter((tag) => tag.endsWith('.url')) : allTags
1531-
} else {
1532-
const outputPaths = getBlockOutputPaths(accessibleBlock.type, mergedSubBlocks)
1533-
const allTags = outputPaths.map((path) => `${normalizedBlockName}.${path}`)
1534-
blockTags = isSelfReference ? allTags.filter((tag) => tag.endsWith('.url')) : allTags
1535-
}
15361505
} else if (accessibleBlock.type === 'human_in_the_loop') {
15371506
const dynamicOutputs = getBlockOutputPaths(accessibleBlock.type, mergedSubBlocks)
15381507

apps/sim/blocks/blocks/agent.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -769,7 +769,13 @@ Example 3 (Array Input):
769769
outputs: {
770770
content: { type: 'string', description: 'Generated response content' },
771771
model: { type: 'string', description: 'Model used for generation' },
772-
tokens: { type: 'any', description: 'Token usage statistics' },
773-
toolCalls: { type: 'any', description: 'Tool calls made' },
772+
tokens: { type: 'json', description: 'Token usage statistics' },
773+
toolCalls: { type: 'json', description: 'Tool calls made' },
774+
providerTiming: {
775+
type: 'json',
776+
description: 'Provider timing information',
777+
hiddenFromDisplay: true,
778+
},
779+
cost: { type: 'number', description: 'Cost of the API call', hiddenFromDisplay: true },
774780
},
775781
}

apps/sim/blocks/blocks/human_in_the_loop.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,5 +162,21 @@ export const HumanInTheLoopBlock: BlockConfig<ResponseBlockOutput> = {
162162
type: 'string',
163163
description: 'Resume API endpoint URL for direct curl requests',
164164
},
165+
response: {
166+
type: 'json',
167+
description: 'Display data shown to the approver',
168+
hiddenFromDisplay: true,
169+
},
170+
submission: {
171+
type: 'json',
172+
description: 'Form submission data from the approver',
173+
hiddenFromDisplay: true,
174+
},
175+
resumeInput: {
176+
type: 'json',
177+
description: 'Raw input data submitted when resuming',
178+
hiddenFromDisplay: true,
179+
},
180+
submittedAt: { type: 'string', description: 'ISO timestamp when the workflow was resumed' },
165181
},
166182
}

apps/sim/blocks/blocks/router.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ export const RouterBlock: BlockConfig<RouterResponse> = {
247247
tokens: { type: 'json', description: 'Token usage' },
248248
cost: { type: 'json', description: 'Cost information' },
249249
selectedPath: { type: 'json', description: 'Selected routing path' },
250+
selectedRoute: { type: 'string', description: 'Selected route ID' },
250251
},
251252
}
252253

apps/sim/blocks/blocks/workflow.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ export const WorkflowBlock: BlockConfig = {
4444
childWorkflowName: { type: 'string', description: 'Child workflow name' },
4545
result: { type: 'json', description: 'Workflow execution result' },
4646
error: { type: 'string', description: 'Error message' },
47+
childTraceSpans: {
48+
type: 'json',
49+
description: 'Child workflow trace spans',
50+
hiddenFromDisplay: true,
51+
},
4752
},
4853
hideFromToolbar: true,
4954
}

apps/sim/blocks/blocks/workflow_input.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,10 @@ export const WorkflowInputBlock: BlockConfig = {
4343
childWorkflowName: { type: 'string', description: 'Child workflow name' },
4444
result: { type: 'json', description: 'Workflow execution result' },
4545
error: { type: 'string', description: 'Error message' },
46+
childTraceSpans: {
47+
type: 'json',
48+
description: 'Child workflow trace spans',
49+
hiddenFromDisplay: true,
50+
},
4651
},
4752
}

apps/sim/blocks/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,11 @@ export type OutputFieldDefinition =
157157
* Uses the same condition format as subBlocks.
158158
*/
159159
condition?: OutputCondition
160+
/**
161+
* If true, this output is hidden from display in the tag dropdown and logs,
162+
* but still available for resolution and execution.
163+
*/
164+
hiddenFromDisplay?: boolean
160165
}
161166

162167
export interface ParamConfig {

apps/sim/executor/constants.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
import type { LoopType, ParallelType } from '@/lib/workflows/types'
22

3+
/**
4+
* Runtime-injected keys for trigger blocks that should be hidden from logs/display.
5+
* These are added during execution but aren't part of the block's static output schema.
6+
*/
7+
export const TRIGGER_INTERNAL_KEYS = ['webhook', 'workflowId'] as const
8+
export type TriggerInternalKey = (typeof TRIGGER_INTERNAL_KEYS)[number]
9+
10+
export function isTriggerInternalKey(key: string): key is TriggerInternalKey {
11+
return TRIGGER_INTERNAL_KEYS.includes(key as TriggerInternalKey)
12+
}
13+
314
export enum BlockType {
415
PARALLEL = 'parallel',
516
LOOP = 'loop',

apps/sim/executor/execution/block-executor.ts

Lines changed: 7 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ import {
1111
DEFAULTS,
1212
EDGE,
1313
isSentinelBlockType,
14-
isTriggerBehavior,
15-
isWorkflowBlockType,
1614
} from '@/executor/constants'
1715
import type { DAGNode } from '@/executor/dag/builder'
1816
import { ChildWorkflowError } from '@/executor/errors/child-workflow-error'
@@ -30,6 +28,7 @@ import type {
3028
} from '@/executor/types'
3129
import { streamingResponseFormatProcessor } from '@/executor/utils'
3230
import { buildBlockExecutionError, normalizeError } from '@/executor/utils/errors'
31+
import { filterOutputForLog } from '@/executor/utils/output-filter'
3332
import { validateBlockType } from '@/executor/utils/permission-check'
3433
import type { VariableResolver } from '@/executor/variables/resolver'
3534
import type { SerializedBlock } from '@/serializer/types'
@@ -149,13 +148,15 @@ export class BlockExecutor {
149148
blockLog.endedAt = new Date().toISOString()
150149
blockLog.durationMs = duration
151150
blockLog.success = true
152-
blockLog.output = this.filterOutputForLog(block, normalizedOutput)
151+
blockLog.output = filterOutputForLog(block.metadata?.id || '', normalizedOutput, { block })
153152
}
154153

155154
this.state.setBlockOutput(node.id, normalizedOutput, duration)
156155

157156
if (!isSentinel) {
158-
const displayOutput = this.filterOutputForDisplay(block, normalizedOutput)
157+
const displayOutput = filterOutputForLog(block.metadata?.id || '', normalizedOutput, {
158+
block,
159+
})
159160
this.callOnBlockComplete(ctx, node, block, resolvedInputs, displayOutput, duration)
160161
}
161162

@@ -233,7 +234,7 @@ export class BlockExecutor {
233234
blockLog.success = false
234235
blockLog.error = errorMessage
235236
blockLog.input = input
236-
blockLog.output = this.filterOutputForLog(block, errorOutput)
237+
blockLog.output = filterOutputForLog(block.metadata?.id || '', errorOutput, { block })
237238
}
238239

239240
logger.error(
@@ -246,7 +247,7 @@ export class BlockExecutor {
246247
)
247248

248249
if (!isSentinel) {
249-
const displayOutput = this.filterOutputForDisplay(block, errorOutput)
250+
const displayOutput = filterOutputForLog(block.metadata?.id || '', errorOutput, { block })
250251
this.callOnBlockComplete(ctx, node, block, input, displayOutput, duration)
251252
}
252253

@@ -335,51 +336,6 @@ export class BlockExecutor {
335336
return { result: output }
336337
}
337338

338-
private filterOutputForLog(
339-
block: SerializedBlock,
340-
output: NormalizedBlockOutput
341-
): NormalizedBlockOutput {
342-
const blockType = block.metadata?.id
343-
344-
if (blockType === BlockType.HUMAN_IN_THE_LOOP) {
345-
const filtered: NormalizedBlockOutput = {}
346-
for (const [key, value] of Object.entries(output)) {
347-
if (key.startsWith('_')) continue
348-
if (key === 'response') continue
349-
filtered[key] = value
350-
}
351-
return filtered
352-
}
353-
354-
if (isTriggerBehavior(block)) {
355-
const filtered: NormalizedBlockOutput = {}
356-
const internalKeys = ['webhook', 'workflowId']
357-
for (const [key, value] of Object.entries(output)) {
358-
if (internalKeys.includes(key)) continue
359-
filtered[key] = value
360-
}
361-
return filtered
362-
}
363-
364-
return output
365-
}
366-
367-
private filterOutputForDisplay(
368-
block: SerializedBlock,
369-
output: NormalizedBlockOutput
370-
): NormalizedBlockOutput {
371-
const filtered = this.filterOutputForLog(block, output)
372-
373-
if (isWorkflowBlockType(block.metadata?.id)) {
374-
const { childTraceSpans: _, ...displayOutput } = filtered as {
375-
childTraceSpans?: unknown
376-
} & Record<string, unknown>
377-
return displayOutput
378-
}
379-
380-
return filtered
381-
}
382-
383339
private callOnBlockStart(ctx: ExecutionContext, node: DAGNode, block: SerializedBlock): void {
384340
const blockId = node.id
385341
const blockName = block.metadata?.name ?? blockId

0 commit comments

Comments
 (0)