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: 4 additions & 0 deletions apps/sim/app/api/workflows/[id]/execute/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
blockId: string,
blockName: string,
blockType: string,
executionOrder: number,
iterationContext?: IterationContext
) => {
logger.info(`[${requestId}] 🔷 onBlockStart called:`, { blockId, blockName, blockType })
Expand All @@ -579,6 +580,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
blockId,
blockName,
blockType,
executionOrder,
...(iterationContext && {
iterationCurrent: iterationContext.iterationCurrent,
iterationTotal: iterationContext.iterationTotal,
Expand Down Expand Up @@ -617,6 +619,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
error: callbackData.output.error,
durationMs: callbackData.executionTime || 0,
startedAt: callbackData.startedAt,
executionOrder: callbackData.executionOrder,
endedAt: callbackData.endedAt,
...(iterationContext && {
iterationCurrent: iterationContext.iterationCurrent,
Expand Down Expand Up @@ -644,6 +647,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
output: callbackData.output,
durationMs: callbackData.executionTime || 0,
startedAt: callbackData.startedAt,
executionOrder: callbackData.executionOrder,
endedAt: callbackData.endedAt,
...(iterationContext && {
iterationCurrent: iterationContext.iterationCurrent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -908,8 +908,10 @@ const PopoverContextCapture: React.FC<{
* When in nested folders, goes back one level at a time.
* At the root folder level, closes the folder.
*/
const TagDropdownBackButton: React.FC = () => {
const { isInFolder, closeFolder, colorScheme, size } = usePopoverContext()
const TagDropdownBackButton: React.FC<{ setSelectedIndex: (index: number) => void }> = ({
setSelectedIndex,
}) => {
const { isInFolder, closeFolder, size, isKeyboardNav, setKeyboardNav } = usePopoverContext()
const nestedNav = useNestedNavigation()

if (!isInFolder) return null
Expand All @@ -922,28 +924,31 @@ const TagDropdownBackButton: React.FC = () => {
closeFolder()
}

const handleMouseEnter = () => {
if (isKeyboardNav) return
setKeyboardNav(false)
setSelectedIndex(-1)
}

return (
<div
className={cn(
'flex min-w-0 cursor-pointer items-center gap-[8px] rounded-[6px] px-[6px] font-base',
size === 'sm' ? 'h-[22px] text-[11px]' : 'h-[26px] text-[13px]',
colorScheme === 'inverted'
? 'text-white hover:bg-[#363636] hover:text-white dark:text-[var(--text-primary)] dark:hover:bg-[var(--surface-5)]'
: 'text-[var(--text-primary)] hover:bg-[var(--border-1)]'
)}
role='button'
onClick={handleBackClick}
<PopoverItem
onMouseDown={(e) => {
e.preventDefault()
e.stopPropagation()
handleBackClick(e)
}}
onMouseEnter={handleMouseEnter}
>
<svg
className={size === 'sm' ? 'h-3 w-3' : 'h-3.5 w-3.5'}
className={cn('shrink-0', size === 'sm' ? 'h-3 w-3' : 'h-3.5 w-3.5')}
fill='none'
viewBox='0 0 24 24'
stroke='currentColor'
>
<path strokeLinecap='round' strokeLinejoin='round' strokeWidth={2} d='M15 19l-7-7 7-7' />
</svg>
<span>Back</span>
</div>
<span className='shrink-0'>Back</span>
</PopoverItem>
)
}

Expand Down Expand Up @@ -1961,8 +1966,8 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
onOpenAutoFocus={(e) => e.preventDefault()}
onCloseAutoFocus={(e) => e.preventDefault()}
>
<TagDropdownBackButton />
<PopoverScrollArea ref={scrollAreaRef}>
<TagDropdownBackButton setSelectedIndex={setSelectedIndex} />
{flatTagList.length === 0 ? (
<div className='px-[6px] py-[8px] text-[12px] text-[var(--white)]/60'>
No matching tags found
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,9 @@ export function useTerminalFilters() {
})
}

// Apply sorting by timestamp
// Sort by executionOrder (monotonically increasing integer from server)
result = [...result].sort((a, b) => {
const timeA = new Date(a.timestamp).getTime()
const timeB = new Date(b.timestamp).getTime()
const comparison = timeA - timeB
const comparison = a.executionOrder - b.executionOrder
return sortConfig.direction === 'asc' ? comparison : -comparison
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,9 @@ function buildEntryTree(entries: ConsoleEntry[]): EntryNode[] {
group.blocks.push(entry)
}

// Sort blocks within each iteration by start time ascending (oldest first, top-down)
// Sort blocks within each iteration by executionOrder ascending (oldest first, top-down)
for (const group of iterationGroupsMap.values()) {
group.blocks.sort((a, b) => {
const aStart = new Date(a.startedAt || a.timestamp).getTime()
const bStart = new Date(b.startedAt || b.timestamp).getTime()
return aStart - bStart
})
group.blocks.sort((a, b) => a.executionOrder - b.executionOrder)
}

// Group iterations by iterationType to create subflow parents
Expand Down Expand Up @@ -225,6 +221,8 @@ function buildEntryTree(entries: ConsoleEntry[]): EntryNode[] {
const totalDuration = allBlocks.reduce((sum, b) => sum + (b.durationMs || 0), 0)

// Create synthetic subflow parent entry
// Use the minimum executionOrder from all child blocks for proper ordering
const subflowExecutionOrder = Math.min(...allBlocks.map((b) => b.executionOrder))
const syntheticSubflow: ConsoleEntry = {
id: `subflow-${iterationType}-${firstIteration.blocks[0]?.executionId || 'unknown'}`,
timestamp: new Date(subflowStartMs).toISOString(),
Expand All @@ -234,6 +232,7 @@ function buildEntryTree(entries: ConsoleEntry[]): EntryNode[] {
blockType: iterationType,
executionId: firstIteration.blocks[0]?.executionId,
startedAt: new Date(subflowStartMs).toISOString(),
executionOrder: subflowExecutionOrder,
endedAt: new Date(subflowEndMs).toISOString(),
durationMs: totalDuration,
success: !allBlocks.some((b) => b.error),
Expand All @@ -251,6 +250,8 @@ function buildEntryTree(entries: ConsoleEntry[]): EntryNode[] {
)
const iterDuration = iterBlocks.reduce((sum, b) => sum + (b.durationMs || 0), 0)

// Use the minimum executionOrder from blocks in this iteration
const iterExecutionOrder = Math.min(...iterBlocks.map((b) => b.executionOrder))
const syntheticIteration: ConsoleEntry = {
id: `iteration-${iterationType}-${iterGroup.iterationCurrent}-${iterBlocks[0]?.executionId || 'unknown'}`,
timestamp: new Date(iterStartMs).toISOString(),
Expand All @@ -260,6 +261,7 @@ function buildEntryTree(entries: ConsoleEntry[]): EntryNode[] {
blockType: iterationType,
executionId: iterBlocks[0]?.executionId,
startedAt: new Date(iterStartMs).toISOString(),
executionOrder: iterExecutionOrder,
endedAt: new Date(iterEndMs).toISOString(),
durationMs: iterDuration,
success: !iterBlocks.some((b) => b.error),
Expand Down Expand Up @@ -300,14 +302,9 @@ function buildEntryTree(entries: ConsoleEntry[]): EntryNode[] {
nodeType: 'block' as const,
}))

// Combine all nodes and sort by start time ascending (oldest first, top-down)
// Combine all nodes and sort by executionOrder ascending (oldest first, top-down)
const allNodes = [...subflowNodes, ...regularNodes]
allNodes.sort((a, b) => {
const aStart = new Date(a.entry.startedAt || a.entry.timestamp).getTime()
const bStart = new Date(b.entry.startedAt || b.entry.timestamp).getTime()
return aStart - bStart
})

allNodes.sort((a, b) => a.entry.executionOrder - b.entry.executionOrder)
return allNodes
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -926,13 +926,15 @@ export function useWorkflowExecution() {
})

// Add entry to terminal immediately with isRunning=true
// Use server-provided executionOrder to ensure correct sort order
const startedAt = new Date().toISOString()
addConsole({
input: {},
output: undefined,
success: undefined,
durationMs: undefined,
startedAt,
executionOrder: data.executionOrder,
endedAt: undefined,
workflowId: activeWorkflowId,
blockId: data.blockId,
Expand All @@ -948,8 +950,6 @@ export function useWorkflowExecution() {
},

onBlockCompleted: (data) => {
logger.info('onBlockCompleted received:', { data })

activeBlocksSet.delete(data.blockId)
setActiveBlocks(new Set(activeBlocksSet))
setBlockRunStatus(data.blockId, 'success')
Expand All @@ -976,6 +976,7 @@ export function useWorkflowExecution() {
success: true,
durationMs: data.durationMs,
startedAt,
executionOrder: data.executionOrder,
endedAt,
})

Expand All @@ -987,6 +988,7 @@ export function useWorkflowExecution() {
replaceOutput: data.output,
success: true,
durationMs: data.durationMs,
startedAt,
endedAt,
isRunning: false,
// Pass through iteration context for subflow grouping
Expand Down Expand Up @@ -1027,6 +1029,7 @@ export function useWorkflowExecution() {
error: data.error,
durationMs: data.durationMs,
startedAt,
executionOrder: data.executionOrder,
endedAt,
})

Expand All @@ -1039,6 +1042,7 @@ export function useWorkflowExecution() {
success: false,
error: data.error,
durationMs: data.durationMs,
startedAt,
endedAt,
isRunning: false,
// Pass through iteration context for subflow grouping
Expand Down Expand Up @@ -1163,13 +1167,15 @@ export function useWorkflowExecution() {

if (existingLogs.length === 0) {
// No blocks executed yet - this is a pre-execution error
// Use 0 for executionOrder so validation errors appear first
addConsole({
input: {},
output: {},
success: false,
error: data.error,
durationMs: data.duration || 0,
startedAt: new Date(Date.now() - (data.duration || 0)).toISOString(),
executionOrder: 0,
endedAt: new Date().toISOString(),
workflowId: activeWorkflowId,
blockId: 'validation',
Expand Down Expand Up @@ -1237,13 +1243,15 @@ export function useWorkflowExecution() {
blockType = error.blockType || blockType
}

// Use MAX_SAFE_INTEGER so execution errors appear at the end of the log
useTerminalConsoleStore.getState().addConsole({
input: {},
output: {},
success: false,
error: normalizedMessage,
durationMs: 0,
startedAt: new Date().toISOString(),
executionOrder: Number.MAX_SAFE_INTEGER,
endedAt: new Date().toISOString(),
workflowId: activeWorkflowId || '',
blockId,
Expand Down Expand Up @@ -1615,6 +1623,7 @@ export function useWorkflowExecution() {
success: true,
durationMs: data.durationMs,
startedAt,
executionOrder: data.executionOrder,
endedAt,
})

Expand All @@ -1624,6 +1633,7 @@ export function useWorkflowExecution() {
success: true,
durationMs: data.durationMs,
startedAt,
executionOrder: data.executionOrder,
endedAt,
workflowId,
blockId: data.blockId,
Expand Down Expand Up @@ -1653,6 +1663,7 @@ export function useWorkflowExecution() {
output: {},
success: false,
error: data.error,
executionOrder: data.executionOrder,
durationMs: data.durationMs,
startedAt,
endedAt,
Expand All @@ -1665,6 +1676,7 @@ export function useWorkflowExecution() {
error: data.error,
durationMs: data.durationMs,
startedAt,
executionOrder: data.executionOrder,
endedAt,
workflowId,
blockId: data.blockId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export async function executeWorkflowWithFullLogging(
success: true,
durationMs: event.data.durationMs,
startedAt: new Date(Date.now() - event.data.durationMs).toISOString(),
executionOrder: event.data.executionOrder,
endedAt: new Date().toISOString(),
workflowId: activeWorkflowId,
blockId: event.data.blockId,
Expand Down Expand Up @@ -140,6 +141,7 @@ export async function executeWorkflowWithFullLogging(
error: event.data.error,
durationMs: event.data.durationMs,
startedAt: new Date(Date.now() - event.data.durationMs).toISOString(),
executionOrder: event.data.executionOrder,
endedAt: new Date().toISOString(),
workflowId: activeWorkflowId,
blockId: event.data.blockId,
Expand Down
Loading