11import type { ForgeTraceLine , TaskExecutionRecord , TraceLineLevel } from '@oma-forge/shared'
2+ import { filterTraceLinesForTask } from '@oma-forge/shared'
23
34type LiveOutputProps = {
45 readonly tasks : readonly TaskExecutionRecord [ ]
56 readonly traceLines : readonly ForgeTraceLine [ ]
7+ /** When set, only trace and status lines for this DAG node are shown. */
8+ readonly scopeTask ?: TaskExecutionRecord | null
9+ readonly variant ?: 'default' | 'coordinator'
10+ readonly planningDone ?: boolean
611}
712
813const terminalStatuses = new Set ( [ 'completed' , 'failed' , 'skipped' , 'blocked' ] )
@@ -21,17 +26,46 @@ function formatTracePrefix(line: ForgeTraceLine): string {
2126 return parts . length > 0 ? `[${ parts . join ( ':' ) } ] ` : ''
2227}
2328
24- export function LiveOutput ( { tasks, traceLines } : LiveOutputProps ) {
25- const finished = tasks . every ( ( task ) => terminalStatuses . has ( task . status ) )
26- const visibleTrace = traceLines . slice ( - 80 )
29+ function systemMessage (
30+ variant : 'default' | 'coordinator' ,
31+ finished : boolean ,
32+ planningDone ?: boolean ,
33+ ) : string {
34+ if ( variant === 'coordinator' ) {
35+ if ( finished || planningDone ) {
36+ return '[SYSTEM] Coordinator planning finished.'
37+ }
38+ return '[SYSTEM] Coordinator planning in progress.'
39+ }
40+ return finished
41+ ? '[SYSTEM] Task graph execution finished.'
42+ : '[SYSTEM] Task graph execution in progress.'
43+ }
44+
45+ export function LiveOutput ( {
46+ tasks,
47+ traceLines,
48+ scopeTask,
49+ variant = 'default' ,
50+ planningDone,
51+ } : LiveOutputProps ) {
52+ const scopedTasks = scopeTask ? [ scopeTask ] : tasks
53+ const finished =
54+ variant === 'coordinator'
55+ ? Boolean ( planningDone )
56+ : scopedTasks . every ( ( task ) => terminalStatuses . has ( task . status ) )
57+ const filteredTrace = scopeTask
58+ ? filterTraceLinesForTask ( traceLines , scopeTask )
59+ : traceLines
60+ const visibleTrace = filteredTrace . slice ( - 80 )
2761
2862 return (
29- < div className = "bg-surface-container-lowest p-3 font-mono text-[10px] leading-relaxed space-y-1 max-h-64 overflow-y-auto" >
30- < p className = " text-tertiary" >
31- { finished
32- ? '[SYSTEM] Task graph execution finished.'
33- : '[SYSTEM] Task graph execution in progress.' }
34- </ p >
63+ < div
64+ className = { `bg-surface-container-lowest p-3 font-mono text-[10px] leading-relaxed space-y-1 overflow-y-auto ${
65+ variant === 'coordinator' ? 'flex-1 min-h-0 max-h-full' : 'max-h-64'
66+ } ` }
67+ >
68+ < p className = "text-tertiary" > { systemMessage ( variant , finished , planningDone ) } < /p >
3569 { visibleTrace . map ( ( line , index ) => (
3670 < p
3771 key = { `${ line . at } -${ index } ` }
@@ -41,14 +75,15 @@ export function LiveOutput({ tasks, traceLines }: LiveOutputProps) {
4175 { line . message }
4276 </ p >
4377 ) ) }
44- { visibleTrace . length === 0
45- ? tasks . map ( ( task ) => (
78+ { visibleTrace . length === 0 && variant === 'coordinator' ? (
79+ < p className = "text-on-surface-variant" > Waiting for coordinator output…</ p >
80+ ) : null }
81+ { visibleTrace . length === 0 && variant !== 'coordinator'
82+ ? scopedTasks . map ( ( task ) => (
4683 < p
4784 key = { task . id }
4885 className = {
49- task . status === 'failed'
50- ? 'text-error'
51- : 'text-on-surface-variant'
86+ task . status === 'failed' ? 'text-error' : 'text-on-surface-variant'
5287 }
5388 >
5489 [{ task . assignee ?. toUpperCase ( ) ?? 'UNASSIGNED' } ] { task . title } { '->' } { ' ' }
0 commit comments