fix(terminal): start precision (#3078)

* fix(executor): use performance.now() for precise block timing

Replace Date.now() with performance.now() for timing measurements in
the executor to provide sub-millisecond precision. This fixes timing
discrepancies with fast-executing blocks like the start block where
millisecond precision was insufficient.

Changes:
- block-executor.ts: Use performance.now() for block execution timing
- engine.ts: Use performance.now() for overall execution timing

Co-authored-by: emir <emir@simstudio.ai>

* format ms as whole nums,round secs to 2 decimal places and compute all started/ended times on server and passback to clinet

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: waleed <walif6@gmail.com>
This commit is contained in:
Emir Karabeg
2026-01-30 12:52:08 -08:00
committed by GitHub
parent aec0de046b
commit 2c4eb9fecb
12 changed files with 107 additions and 69 deletions

View File

@@ -71,7 +71,7 @@ export class BlockExecutor {
this.callOnBlockStart(ctx, node, block)
}
const startTime = Date.now()
const startTime = performance.now()
let resolvedInputs: Record<string, any> = {}
const nodeMetadata = this.buildNodeMetadata(node)
@@ -145,7 +145,7 @@ export class BlockExecutor {
})) as NormalizedBlockOutput
}
const duration = Date.now() - startTime
const duration = performance.now() - startTime
if (blockLog) {
blockLog.endedAt = new Date().toISOString()
@@ -169,7 +169,9 @@ export class BlockExecutor {
block,
this.sanitizeInputsForLog(resolvedInputs),
displayOutput,
duration
duration,
blockLog!.startedAt,
blockLog!.endedAt
)
}
@@ -221,7 +223,7 @@ export class BlockExecutor {
isSentinel: boolean,
phase: 'input_resolution' | 'execution'
): NormalizedBlockOutput {
const duration = Date.now() - startTime
const duration = performance.now() - startTime
const errorMessage = normalizeError(error)
const hasResolvedInputs =
resolvedInputs && typeof resolvedInputs === 'object' && Object.keys(resolvedInputs).length > 0
@@ -274,7 +276,9 @@ export class BlockExecutor {
block,
this.sanitizeInputsForLog(input),
displayOutput,
duration
duration,
blockLog!.startedAt,
blockLog!.endedAt
)
}
@@ -423,7 +427,9 @@ export class BlockExecutor {
block: SerializedBlock,
input: Record<string, any>,
output: NormalizedBlockOutput,
duration: number
duration: number,
startedAt: string,
endedAt: string
): void {
const blockId = node.id
const blockName = block.metadata?.name ?? blockId
@@ -440,6 +446,8 @@ export class BlockExecutor {
input,
output,
executionTime: duration,
startedAt,
endedAt,
},
iterationContext
)

View File

@@ -101,7 +101,7 @@ export class ExecutionEngine {
}
async run(triggerBlockId?: string): Promise<ExecutionResult> {
const startTime = Date.now()
const startTime = performance.now()
try {
this.initializeQueue(triggerBlockId)
@@ -125,8 +125,8 @@ export class ExecutionEngine {
return this.buildPausedResult(startTime)
}
const endTime = Date.now()
this.context.metadata.endTime = new Date(endTime).toISOString()
const endTime = performance.now()
this.context.metadata.endTime = new Date().toISOString()
this.context.metadata.duration = endTime - startTime
if (this.cancelledFlag) {
@@ -146,8 +146,8 @@ export class ExecutionEngine {
metadata: this.context.metadata,
}
} catch (error) {
const endTime = Date.now()
this.context.metadata.endTime = new Date(endTime).toISOString()
const endTime = performance.now()
this.context.metadata.endTime = new Date().toISOString()
this.context.metadata.duration = endTime - startTime
if (this.cancelledFlag) {
@@ -433,8 +433,8 @@ export class ExecutionEngine {
}
private buildPausedResult(startTime: number): ExecutionResult {
const endTime = Date.now()
this.context.metadata.endTime = new Date(endTime).toISOString()
const endTime = performance.now()
this.context.metadata.endTime = new Date().toISOString()
this.context.metadata.duration = endTime - startTime
this.context.metadata.status = 'paused'

View File

@@ -103,7 +103,13 @@ export interface ContextExtensions {
blockId: string,
blockName: string,
blockType: string,
output: { input?: any; output: NormalizedBlockOutput; executionTime: number },
output: {
input?: any
output: NormalizedBlockOutput
executionTime: number
startedAt: string
endedAt: string
},
iterationContext?: IterationContext
) => Promise<void>

View File

@@ -281,9 +281,12 @@ export class LoopOrchestrator {
// Emit onBlockComplete for the loop container so the UI can track it
if (this.contextExtensions?.onBlockComplete) {
const now = new Date().toISOString()
this.contextExtensions.onBlockComplete(loopId, 'Loop', 'loop', {
output,
executionTime: DEFAULTS.EXECUTION_TIME,
startedAt: now,
endedAt: now,
})
}

View File

@@ -265,9 +265,12 @@ export class ParallelOrchestrator {
// Emit onBlockComplete for the parallel container so the UI can track it
if (this.contextExtensions?.onBlockComplete) {
const now = new Date().toISOString()
this.contextExtensions.onBlockComplete(parallelId, 'Parallel', 'parallel', {
output,
executionTime: 0,
startedAt: now,
endedAt: now,
})
}

View File

@@ -232,6 +232,8 @@ export function addSubflowErrorLog(
input: inputData,
output: { error: errorMessage },
executionTime: 0,
startedAt: now,
endedAt: now,
})
}
}