-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Expand file tree
/
Copy pathtypes.ts
More file actions
611 lines (550 loc) · 16.6 KB
/
Copy pathtypes.ts
File metadata and controls
611 lines (550 loc) · 16.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
import type { BillingAttributionSnapshot } from '@/lib/billing/core/billing-attribution'
import type { TraceSpan } from '@/lib/logs/types'
import type { PermissionGroupConfig } from '@/lib/permission-groups/types'
import type { BlockOutput } from '@/blocks/types'
import type {
ChildWorkflowContext,
IterationContext,
ParentIteration,
PiiBlockOutputRedaction,
SerializableExecutionState,
} from '@/executor/execution/types'
import type { RunFromBlockContext } from '@/executor/utils/run-from-block'
import type { SerializedBlock, SerializedWorkflow } from '@/serializer/types'
import type { SubflowType } from '@/stores/workflows/workflow/types'
export interface UserFile {
id: string
name: string
url: string
size: number
type: string
key: string
context?: string
base64?: string
/** Provider Files API handle (OpenAI/Anthropic `file_...` id) set when a large file is uploaded instead of inlined as base64. */
providerFileId?: string
/** Provider File API uri (Gemini `fileUri`) set when a large file is uploaded instead of inlined as base64. */
providerFileUri?: string
/** Short-lived signed HTTPS URL passed to providers that fetch attachments by remote URL instead of inlining base64. */
remoteUrl?: string
}
export interface ParallelPauseScope {
parallelId: string
branchIndex: number
branchTotal?: number
}
export interface LoopPauseScope {
loopId: string
iteration: number
}
export type PauseKind = 'human' | 'time'
export interface PauseMetadata {
contextId: string
blockId: string
response: any
timestamp: string
parallelScope?: ParallelPauseScope
loopScope?: LoopPauseScope
resumeLinks?: {
apiUrl: string
uiUrl: string
contextId: string
executionId: string
workflowId: string
}
pauseKind: PauseKind
/** ISO timestamp at which a `pauseKind: 'time'` pause becomes due for automatic resume. */
resumeAt?: string
}
export type ResumeStatus = 'paused' | 'resumed' | 'failed' | 'queued' | 'resuming'
export interface PausePoint {
contextId: string
blockId?: string
response: any
registeredAt: string
resumeStatus: ResumeStatus
automaticResumeWaitingReason?: string
snapshotReady: boolean
parallelScope?: ParallelPauseScope
loopScope?: LoopPauseScope
resumeLinks?: {
apiUrl: string
uiUrl: string
contextId: string
executionId: string
workflowId: string
}
pauseKind: PauseKind
resumeAt?: string
}
export interface SerializedSnapshot {
snapshot: string
triggerIds: string[]
}
/**
* Identifies a tool call emitted by a model iteration. Matches the
* `tool_call.id` convention used by OpenAI, Anthropic, and the OTel GenAI
* spec so tool segments can be correlated back to the iteration that issued
* them.
*/
export interface IterationToolCall {
id: string
name: string
arguments: Record<string, unknown> | string
}
/**
* A single phase of provider execution (model call or tool invocation).
*
* Providers emit these per iteration. Model segments carry the assistant's
* output for that iteration (text, thinking, tool_calls, tokens, finish
* reason) so the trace reveals *why* each tool was invoked — not just that
* it was. All content fields are optional; providers fill in what they have.
*/
export interface ProviderTimingSegment {
type: 'model' | 'tool'
name?: string
startTime: number
endTime: number
duration: number
assistantContent?: string
thinkingContent?: string
toolCalls?: IterationToolCall[]
toolCallId?: string
finishReason?: string
tokens?: BlockTokens
/** Cost for this segment in USD, derived from tokens + model pricing. */
cost?: { input?: number; output?: number; total?: number }
/** Time-to-first-token in ms (streaming only; first segment typically). */
ttft?: number
/** Provider system identifier (anthropic, openai, gemini, etc.) — `gen_ai.system`. */
provider?: string
/** Structured error class (e.g. `rate_limit`, `context_length`). */
errorType?: string
/** Human-readable error message when this segment failed. */
errorMessage?: string
}
/** Timing info reported by an LLM provider for a single block execution. */
interface BlockProviderTiming {
startTime: string
endTime: string
duration: number
modelTime?: number
toolsTime?: number
firstResponseTime?: number
iterations?: number
timeSegments?: ProviderTimingSegment[]
}
/** Cost breakdown from provider usage. */
interface BlockCost {
input: number
output: number
total: number
toolCost?: number
pricing?: {
input: number
output: number
cachedInput?: number
updatedAt: string
}
}
/** Token usage from provider. `prompt`/`completion` are legacy aliases. */
export interface BlockTokens {
input?: number
output?: number
total?: number
prompt?: number
completion?: number
/** Input tokens served from the provider's prompt cache. */
cacheRead?: number
/** Input tokens newly written to the provider's prompt cache. */
cacheWrite?: number
/** Output tokens consumed by reasoning/thinking (o-series, Claude, Gemini). */
reasoning?: number
}
/** A single tool invocation recorded by an agent-type block. */
export interface BlockToolCall {
name: string
duration?: number
startTime?: string
endTime?: string
error?: string
arguments?: Record<string, unknown>
input?: Record<string, unknown>
result?: Record<string, unknown>
output?: Record<string, unknown>
}
/** Normalized tool-call container emitted by providers. */
interface BlockToolCalls {
list: BlockToolCall[]
count: number
}
export interface NormalizedBlockOutput {
[key: string]: any
content?: string
model?: string
tokens?: BlockTokens
toolCalls?: BlockToolCalls
providerTiming?: BlockProviderTiming
cost?: BlockCost
files?: UserFile[]
selectedPath?: {
blockId: string
blockType?: string
blockTitle?: string
}
selectedOption?: string
conditionResult?: boolean
result?: any
stdout?: string
executionTime?: number
data?: any
status?: number
headers?: Record<string, string>
error?: string
childTraceSpans?: TraceSpan[]
childWorkflowName?: string
_pauseMetadata?: PauseMetadata
}
export const EXECUTION_CONTROL_OUTPUT_FIELD_NAMES = [
'error',
'selectedOption',
'selectedRoute',
'_pauseMetadata',
] as const
export type ExecutionControlOutputFieldName = (typeof EXECUTION_CONTROL_OUTPUT_FIELD_NAMES)[number]
/** Start block output key that carries trusted, server-injected run metadata. */
export const START_BLOCK_METADATA_FIELD = 'metadata'
/**
* Trusted run metadata surfaced under `<start.metadata.*>` when the Start
* block's "Add run metadata" toggle is enabled. Built server-side from the
* authenticated execution context — never from caller-supplied input.
* Every field describes the INVOKING run: on top-level runs that is the run
* itself; on child and custom-block executions it is the parent run (its
* actor's email, workspace, and workflow) — never the child's own static,
* authoring-time-known identity.
*/
export interface StartBlockRunMetadata {
userEmail?: string | null
workspaceId?: string | null
workflowId?: string | null
executionId?: string
executionType?: string
executionMode?: 'sync' | 'stream' | 'async'
startTime?: string
}
export interface BlockLog {
blockId: string
blockName?: string
blockType?: string
startedAt: string
endedAt: string
durationMs: number
success: boolean
output?: NormalizedBlockOutput
input?: Record<string, unknown>
error?: string
/** Whether this error was handled by an error handler path (error port) */
errorHandled?: boolean
loopId?: string
parallelId?: string
iterationIndex?: number
/** Full ancestor iteration chain for nested subflows (outermost → innermost). */
parentIterations?: ParentIteration[]
/**
* Monotonically increasing integer (1, 2, 3, ...) for accurate block ordering.
* Generated via getNextExecutionOrder() to ensure deterministic sorting.
*/
executionOrder: number
/**
* Child workflow trace spans for nested workflow execution.
* Stored separately from output to keep output clean for display
* while preserving data for trace-spans processing.
*/
childTraceSpans?: TraceSpan[]
}
interface ExecutionMetadata {
requestId?: string
workflowId?: string
workspaceId?: string
/** Immutable actor/payer decision captured before execution. */
billingAttribution?: BillingAttributionSnapshot
startTime?: string
endTime?: string
duration: number
pendingBlocks?: string[]
isDebugSession?: boolean
context?: ExecutionContext
workflowConnections?: Array<{ source: string; target: string }>
credentialAccountUserId?: string
largeValueKeys?: string[]
fileKeys?: string[]
status?: 'running' | 'paused' | 'completed'
pausePoints?: string[]
resumeChain?: {
parentExecutionId?: string
depth: number
}
userId?: string
executionId?: string
triggerType?: string
triggerBlockId?: string
useDraftState?: boolean
resumeFromSnapshot?: boolean
resumeTerminalNoop?: boolean
executionMode?: 'sync' | 'stream' | 'async'
}
export interface BlockState {
output: NormalizedBlockOutput
executed: boolean
executionTime: number
}
export interface ExecutionContext {
workflowId: string
workspaceId?: string
executionId?: string
largeValueExecutionIds?: string[]
largeValueKeys?: string[]
fileKeys?: string[]
allowLargeValueWorkflowScope?: boolean
userId?: string
isDeployedContext?: boolean
enforceCredentialAccess?: boolean
copilotToolExecution?: boolean
/** In-flight block-output PII redaction policy (resolved `blockOutputs` stage). */
piiBlockOutputRedaction?: PiiBlockOutputRedaction
permissionConfig?: PermissionGroupConfig | null
permissionConfigLoaded?: boolean
blockStates: ReadonlyMap<string, BlockState>
executedBlocks: ReadonlySet<string>
blockLogs: BlockLog[]
metadata: ExecutionMetadata
/** Trusted run metadata for the Start block's "Add run metadata" toggle. */
startRunMetadata?: StartBlockRunMetadata
environmentVariables: Record<string, string>
workflowVariables?: Record<string, any>
decisions: {
router: Map<string, string>
condition: Map<string, string>
}
completedLoops: Set<string>
/**
* Unified parent map for subflow nesting (loop-in-loop, parallel-in-parallel,
* loop-in-parallel, parallel-in-loop). Maps any child subflow ID to its parent
* subflow ID and type, enabling the iteration context builder to walk the full
* ancestor chain regardless of subflow type.
*/
subflowParentMap?: Map<
string,
{ parentId: string; parentType: SubflowType; branchIndex?: number }
>
loopExecutions?: Map<
string,
{
iteration: number
currentIterationOutputs: Map<string, any>
allIterationOutputs: any[][]
maxIterations?: number
item?: any
items?: any[]
condition?: string
skipFirstConditionCheck?: boolean
skippedAtStart?: boolean
loopType?: 'for' | 'forEach' | 'while' | 'doWhile'
}
>
parallelExecutions?: Map<
string,
{
parallelId: string
totalBranches: number
batchSize?: number
currentBatchStart?: number
currentBatchSize?: number
accumulatedOutputs?: Map<number, any[]>
branchOutputs: Map<number, any[]>
parallelType?: 'count' | 'collection'
items?: any[]
validationError?: string
isEmpty?: boolean
}
>
parallelBlockMapping?: Map<
string,
{
originalBlockId: string
parallelId: string
iterationIndex: number
}
>
currentVirtualBlockId?: string
activeExecutionPath: Set<string>
workflow?: SerializedWorkflow
stream?: boolean
selectedOutputs?: string[]
edges?: Array<{ source: string; target: string }>
onStream?: (streamingExecution: StreamingExecution) => Promise<void>
onBlockStart?: (
blockId: string,
blockName: string,
blockType: string,
executionOrder: number,
iterationContext?: IterationContext,
childWorkflowContext?: ChildWorkflowContext
) => Promise<void>
onBlockComplete?: (
blockId: string,
blockName: string,
blockType: string,
output: any,
iterationContext?: IterationContext,
childWorkflowContext?: ChildWorkflowContext
) => Promise<void>
/** Context identifying this execution as a child of a workflow block */
childWorkflowContext?: ChildWorkflowContext
/** Fires immediately after instanceId is generated, before child execution begins. */
onChildWorkflowInstanceReady?: (
blockId: string,
childWorkflowInstanceId: string,
iterationContext?: IterationContext,
executionOrder?: number,
childWorkflowContext?: ChildWorkflowContext
) => Promise<void>
/**
* AbortSignal for cancellation support.
* When the signal is aborted, execution should stop gracefully.
* This is triggered when the SSE client disconnects.
*/
abortSignal?: AbortSignal
/**
* When true, UserFile objects in block outputs will be hydrated with base64 content
* before being stored in execution state. This ensures base64 is available for
* variable resolution in downstream blocks.
*/
includeFileBase64?: boolean
/**
* Maximum file size in bytes for base64 hydration. Files larger than this limit
* will not have their base64 content fetched.
*/
base64MaxBytes?: number
/**
* Context for "run from block" mode. When present, only blocks in dirtySet
* will be executed; others return cached outputs from the source snapshot.
*/
runFromBlockContext?: RunFromBlockContext
/**
* Stop execution after this block completes. Used for "run until block" feature.
*/
stopAfterBlockId?: string
/**
* Ordered list of workflow IDs in the current call chain, used for cycle detection.
* Passed to outgoing HTTP requests via the X-Sim-Via header.
*/
callChain?: string[]
/**
* Counter for generating monotonically increasing execution order values.
* Starts at 0 and increments for each block. Use getNextExecutionOrder() to access.
*/
executionOrderCounter?: { value: number }
}
/**
* Gets the next execution order value for a block.
* Returns a simple incrementing integer (1, 2, 3, ...) for clear ordering.
*/
export function getNextExecutionOrder(ctx: ExecutionContext): number {
if (!ctx.executionOrderCounter) {
ctx.executionOrderCounter = { value: 0 }
}
return ++ctx.executionOrderCounter.value
}
export interface ExecutionResult {
success: boolean
output: NormalizedBlockOutput
error?: string
logs?: BlockLog[]
executionState?: SerializableExecutionState
metadata?: ExecutionMetadata
status?: 'completed' | 'paused' | 'cancelled'
pausePoints?: PausePoint[]
snapshotSeed?: SerializedSnapshot
_streamingMetadata?: {
loggingSession: any
processedInput: any
}
}
export interface StreamingExecution {
stream: ReadableStream
execution: ExecutionResult & { isStreaming?: boolean }
/**
* Invoked with the assembled response text after the stream drains. Lets agent
* blocks persist the full response without interposing a TransformStream on a
* fetch-backed source — that pattern amplifies memory on Bun via #28035.
*/
onFullContent?: (content: string) => void | Promise<void>
}
interface BlockExecutor {
canExecute(block: SerializedBlock): boolean
execute(
block: SerializedBlock,
inputs: Record<string, any>,
context: ExecutionContext
): Promise<BlockOutput>
}
export interface BlockHandler {
canHandle(block: SerializedBlock): boolean
execute(
ctx: ExecutionContext,
block: SerializedBlock,
inputs: Record<string, any>
): Promise<BlockOutput | StreamingExecution>
executeWithNode?: (
ctx: ExecutionContext,
block: SerializedBlock,
inputs: Record<string, any>,
nodeMetadata: {
nodeId: string
loopId?: string
parallelId?: string
branchIndex?: number
branchTotal?: number
originalBlockId?: string
isLoopNode?: boolean
executionOrder?: number
}
) => Promise<BlockOutput | StreamingExecution>
}
interface Tool<P = any, O = Record<string, any>> {
id: string
name: string
description: string
version: string
params: {
[key: string]: {
type: string
required?: boolean
description?: string
default?: any
}
}
request?: {
url?: string | ((params: P) => string)
method?: string
headers?: (params: P) => Record<string, string>
body?: (params: P) => Record<string, any>
}
transformResponse?: (response: any) => Promise<{
success: boolean
output: O
error?: string
}>
}
interface ToolRegistry {
[key: string]: Tool
}
export interface ResponseFormatStreamProcessor {
processStream(
originalStream: ReadableStream,
blockId: string,
selectedOutputs: string[],
responseFormat?: any
): ReadableStream
}