agyqueue 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1155 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>AgyQueue Workflow Console</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
8
+ <style>
9
+ :root {
10
+ --bg-primary: #0b0f19;
11
+ --bg-secondary: #111827;
12
+ --bg-tertiary: #1f2937;
13
+ --bg-elevated: #1e293b;
14
+ --text-primary: #f9fafb;
15
+ --text-secondary: #9ca3af;
16
+ --text-muted: #6b7280;
17
+ --accent-primary: #6366f1; /* Indigo */
18
+ --accent-hover: #4f46e5;
19
+ --success: #10b981;
20
+ --danger: #ef4444;
21
+ --warning: #f59e0b;
22
+ --border-color: #374151;
23
+ --sidebar-width: 280px;
24
+ }
25
+
26
+ * {
27
+ box-sizing: border-box;
28
+ margin: 0;
29
+ padding: 0;
30
+ }
31
+
32
+ body {
33
+ font-family: 'Inter', sans-serif;
34
+ background-color: var(--bg-primary);
35
+ color: var(--text-primary);
36
+ display: flex;
37
+ height: 100vh;
38
+ overflow: hidden;
39
+ }
40
+
41
+ /* Sidebar Styling */
42
+ .sidebar {
43
+ width: var(--sidebar-width);
44
+ background-color: var(--bg-secondary);
45
+ border-right: 1px solid var(--border-color);
46
+ display: flex;
47
+ flex-direction: column;
48
+ height: 100%;
49
+ }
50
+
51
+ .brand-header {
52
+ padding: 20px 24px;
53
+ border-bottom: 1px solid var(--border-color);
54
+ display: flex;
55
+ align-items: center;
56
+ gap: 12px;
57
+ }
58
+
59
+ .brand-logo {
60
+ width: 28px;
61
+ height: 28px;
62
+ background: linear-gradient(135deg, var(--accent-primary), #3b82f6);
63
+ border-radius: 6px;
64
+ display: flex;
65
+ align-items: center;
66
+ justify-content: center;
67
+ font-weight: 700;
68
+ color: white;
69
+ font-size: 14px;
70
+ box-shadow: 0 0 12px rgba(99, 102, 241, 0.4);
71
+ }
72
+
73
+ .brand-title {
74
+ font-weight: 600;
75
+ font-size: 16px;
76
+ letter-spacing: -0.01em;
77
+ color: var(--text-primary);
78
+ }
79
+
80
+ .brand-badge {
81
+ font-size: 9px;
82
+ background-color: rgba(99, 102, 241, 0.2);
83
+ color: #a5b4fc;
84
+ padding: 1px 6px;
85
+ border-radius: 4px;
86
+ font-weight: 500;
87
+ border: 1px solid rgba(99, 102, 241, 0.3);
88
+ }
89
+
90
+ .sidebar-section {
91
+ padding: 20px 24px 10px;
92
+ font-size: 11px;
93
+ text-transform: uppercase;
94
+ letter-spacing: 0.05em;
95
+ color: var(--text-muted);
96
+ font-weight: 600;
97
+ }
98
+
99
+ .task-submitter-box {
100
+ padding: 0 20px 20px;
101
+ border-bottom: 1px solid var(--border-color);
102
+ }
103
+
104
+ .form-group {
105
+ margin-bottom: 14px;
106
+ display: flex;
107
+ flex-direction: column;
108
+ gap: 6px;
109
+ }
110
+
111
+ .form-group label {
112
+ font-size: 11px;
113
+ font-weight: 500;
114
+ color: var(--text-secondary);
115
+ }
116
+
117
+ .form-group textarea, .form-group select {
118
+ width: 100%;
119
+ background-color: var(--bg-primary);
120
+ border: 1px solid var(--border-color);
121
+ border-radius: 6px;
122
+ padding: 10px;
123
+ color: var(--text-primary);
124
+ font-family: inherit;
125
+ font-size: 12px;
126
+ outline: none;
127
+ transition: border-color 0.2s;
128
+ }
129
+
130
+ .form-group textarea:focus, .form-group select:focus {
131
+ border-color: var(--accent-primary);
132
+ }
133
+
134
+ .btn {
135
+ width: 100%;
136
+ background-color: var(--accent-primary);
137
+ color: white;
138
+ border: none;
139
+ border-radius: 6px;
140
+ padding: 10px;
141
+ font-size: 13px;
142
+ font-weight: 500;
143
+ cursor: pointer;
144
+ transition: background-color 0.2s;
145
+ display: flex;
146
+ align-items: center;
147
+ justify-content: center;
148
+ gap: 8px;
149
+ }
150
+
151
+ .btn:hover {
152
+ background-color: var(--accent-hover);
153
+ }
154
+
155
+ .btn-secondary {
156
+ background-color: transparent;
157
+ border: 1px solid var(--border-color);
158
+ color: var(--text-secondary);
159
+ }
160
+
161
+ .btn-secondary:hover {
162
+ background-color: var(--bg-tertiary);
163
+ color: var(--text-primary);
164
+ }
165
+
166
+ .btn-danger {
167
+ background-color: rgba(239, 68, 68, 0.15);
168
+ border: 1px solid rgba(239, 68, 68, 0.3);
169
+ color: #fca5a5;
170
+ }
171
+
172
+ .btn-danger:hover {
173
+ background-color: var(--danger);
174
+ color: white;
175
+ }
176
+
177
+ /* Main Console Workspace */
178
+ .workspace {
179
+ flex: 1;
180
+ display: flex;
181
+ flex-direction: column;
182
+ height: 100%;
183
+ background-color: var(--bg-primary);
184
+ }
185
+
186
+ .console-header {
187
+ height: 69px;
188
+ background-color: var(--bg-secondary);
189
+ border-bottom: 1px solid var(--border-color);
190
+ padding: 0 32px;
191
+ display: flex;
192
+ align-items: center;
193
+ justify-content: space-between;
194
+ }
195
+
196
+ .console-path {
197
+ font-size: 14px;
198
+ font-weight: 500;
199
+ display: flex;
200
+ align-items: center;
201
+ gap: 8px;
202
+ color: var(--text-secondary);
203
+ }
204
+
205
+ .console-path span.active {
206
+ color: var(--text-primary);
207
+ font-weight: 600;
208
+ }
209
+
210
+ /* Filter Controls */
211
+ .grid-filters {
212
+ padding: 20px 32px;
213
+ display: flex;
214
+ align-items: center;
215
+ justify-content: space-between;
216
+ background-color: var(--bg-primary);
217
+ border-bottom: 1px solid var(--border-color);
218
+ gap: 20px;
219
+ }
220
+
221
+ .search-wrapper {
222
+ position: relative;
223
+ flex: 1;
224
+ max-width: 400px;
225
+ }
226
+
227
+ .search-wrapper svg {
228
+ position: absolute;
229
+ left: 12px;
230
+ top: 50%;
231
+ transform: translateY(-50%);
232
+ width: 16px;
233
+ height: 16px;
234
+ stroke: var(--text-muted);
235
+ }
236
+
237
+ .search-input {
238
+ width: 100%;
239
+ background-color: var(--bg-secondary);
240
+ border: 1px solid var(--border-color);
241
+ border-radius: 6px;
242
+ padding: 8px 12px 8px 36px;
243
+ color: var(--text-primary);
244
+ font-size: 13px;
245
+ outline: none;
246
+ }
247
+
248
+ .search-input:focus {
249
+ border-color: var(--accent-primary);
250
+ }
251
+
252
+ .status-tabs {
253
+ display: flex;
254
+ background-color: var(--bg-secondary);
255
+ padding: 3px;
256
+ border-radius: 6px;
257
+ border: 1px solid var(--border-color);
258
+ }
259
+
260
+ .status-tab {
261
+ padding: 6px 12px;
262
+ font-size: 12px;
263
+ font-weight: 500;
264
+ color: var(--text-secondary);
265
+ border-radius: 4px;
266
+ cursor: pointer;
267
+ transition: all 0.2s;
268
+ display: flex;
269
+ align-items: center;
270
+ gap: 6px;
271
+ }
272
+
273
+ .status-tab.active {
274
+ background-color: var(--bg-tertiary);
275
+ color: var(--text-primary);
276
+ }
277
+
278
+ .status-count {
279
+ font-size: 10px;
280
+ background-color: rgba(255, 255, 255, 0.1);
281
+ padding: 1px 6px;
282
+ border-radius: 10px;
283
+ color: var(--text-secondary);
284
+ }
285
+
286
+ /* Workflows Grid Panel */
287
+ .content-panel {
288
+ flex: 1;
289
+ display: flex;
290
+ overflow: hidden;
291
+ }
292
+
293
+ .workflows-grid-container {
294
+ flex: 1;
295
+ overflow-y: auto;
296
+ border-right: 1px solid var(--border-color);
297
+ }
298
+
299
+ .workflow-table {
300
+ width: 100%;
301
+ border-collapse: collapse;
302
+ text-align: left;
303
+ }
304
+
305
+ .workflow-table th {
306
+ font-size: 11px;
307
+ text-transform: uppercase;
308
+ color: var(--text-muted);
309
+ font-weight: 600;
310
+ padding: 12px 24px;
311
+ border-bottom: 1px solid var(--border-color);
312
+ background-color: rgba(17, 24, 39, 0.4);
313
+ letter-spacing: 0.05em;
314
+ }
315
+
316
+ .workflow-table td {
317
+ padding: 16px 24px;
318
+ border-bottom: 1px solid var(--border-color);
319
+ font-size: 13px;
320
+ vertical-align: middle;
321
+ }
322
+
323
+ .workflow-row {
324
+ cursor: pointer;
325
+ transition: background-color 0.2s;
326
+ }
327
+
328
+ .workflow-row:hover {
329
+ background-color: rgba(31, 41, 55, 0.3);
330
+ }
331
+
332
+ .workflow-row.selected {
333
+ background-color: rgba(99, 102, 241, 0.08);
334
+ border-left: 3px solid var(--accent-primary);
335
+ }
336
+
337
+ .workflow-id-col {
338
+ font-family: 'JetBrains Mono', monospace;
339
+ font-weight: 500;
340
+ color: var(--accent-primary);
341
+ }
342
+
343
+ .workflow-type-badge {
344
+ font-family: 'JetBrains Mono', monospace;
345
+ font-size: 11px;
346
+ background-color: var(--bg-tertiary);
347
+ padding: 3px 8px;
348
+ border-radius: 4px;
349
+ color: var(--text-secondary);
350
+ border: 1px solid var(--border-color);
351
+ }
352
+
353
+ /* Badges Styling */
354
+ .status-badge {
355
+ font-size: 10px;
356
+ font-weight: 600;
357
+ text-transform: uppercase;
358
+ padding: 4px 10px;
359
+ border-radius: 12px;
360
+ display: inline-flex;
361
+ align-items: center;
362
+ gap: 6px;
363
+ }
364
+
365
+ .status-badge-queued { background-color: rgba(245, 158, 11, 0.1); color: var(--warning); border: 1px solid rgba(245, 158, 11, 0.2); }
366
+ .status-badge-running { background-color: rgba(59, 130, 246, 0.1); color: var(--accent-primary); border: 1px solid rgba(59, 130, 246, 0.2); }
367
+ .status-badge-waiting { background-color: rgba(129, 140, 248, 0.1); color: #818cf8; border: 1px solid rgba(129, 140, 248, 0.2); }
368
+ .status-badge-completed { background-color: rgba(16, 185, 129, 0.1); color: var(--success); border: 1px solid rgba(16, 185, 129, 0.2); }
369
+ .status-badge-failed { background-color: rgba(239, 68, 68, 0.1); color: var(--danger); border: 1px solid rgba(239, 68, 68, 0.2); }
370
+ .status-badge-cancelled { background-color: rgba(107, 114, 128, 0.1); color: var(--text-secondary); border: 1px solid rgba(107, 114, 128, 0.2); }
371
+
372
+ /* Details Pane Styling */
373
+ .details-pane {
374
+ width: 48%;
375
+ min-width: 480px;
376
+ background-color: var(--bg-secondary);
377
+ display: flex;
378
+ flex-direction: column;
379
+ overflow: hidden;
380
+ height: 100%;
381
+ }
382
+
383
+ .details-pane-placeholder {
384
+ flex: 1;
385
+ display: flex;
386
+ flex-direction: column;
387
+ align-items: center;
388
+ justify-content: center;
389
+ color: var(--text-muted);
390
+ gap: 16px;
391
+ padding: 40px;
392
+ text-align: center;
393
+ }
394
+
395
+ .details-pane-placeholder svg {
396
+ width: 48px;
397
+ height: 48px;
398
+ stroke: var(--text-muted);
399
+ }
400
+
401
+ .details-header {
402
+ padding: 24px;
403
+ border-bottom: 1px solid var(--border-color);
404
+ display: flex;
405
+ flex-direction: column;
406
+ gap: 16px;
407
+ }
408
+
409
+ .details-title-row {
410
+ display: flex;
411
+ align-items: center;
412
+ justify-content: space-between;
413
+ }
414
+
415
+ .details-workflow-id {
416
+ font-family: 'JetBrains Mono', monospace;
417
+ font-size: 18px;
418
+ font-weight: 600;
419
+ }
420
+
421
+ .details-actions {
422
+ display: flex;
423
+ gap: 8px;
424
+ }
425
+
426
+ .details-meta-grid {
427
+ display: grid;
428
+ grid-template-columns: repeat(2, 1fr);
429
+ gap: 16px;
430
+ background-color: rgba(17, 24, 39, 0.4);
431
+ padding: 16px;
432
+ border-radius: 8px;
433
+ border: 1px solid var(--border-color);
434
+ }
435
+
436
+ .meta-item {
437
+ display: flex;
438
+ flex-direction: column;
439
+ gap: 4px;
440
+ }
441
+
442
+ .meta-label {
443
+ font-size: 10px;
444
+ text-transform: uppercase;
445
+ color: var(--text-muted);
446
+ font-weight: 600;
447
+ letter-spacing: 0.05em;
448
+ }
449
+
450
+ .meta-value {
451
+ font-size: 13px;
452
+ font-weight: 500;
453
+ color: var(--text-primary);
454
+ }
455
+
456
+ .meta-value-mono {
457
+ font-family: 'JetBrains Mono', monospace;
458
+ color: var(--accent-primary);
459
+ cursor: pointer;
460
+ text-decoration: underline;
461
+ }
462
+
463
+ .details-body {
464
+ flex: 1;
465
+ overflow-y: auto;
466
+ padding: 24px;
467
+ display: flex;
468
+ flex-direction: column;
469
+ gap: 24px;
470
+ }
471
+
472
+ /* Timeline Event History */
473
+ .history-timeline {
474
+ display: flex;
475
+ flex-direction: column;
476
+ position: relative;
477
+ padding-left: 20px;
478
+ }
479
+
480
+ .history-timeline::before {
481
+ content: '';
482
+ position: absolute;
483
+ left: 5px;
484
+ top: 6px;
485
+ bottom: 6px;
486
+ width: 2px;
487
+ background-color: var(--border-color);
488
+ }
489
+
490
+ .timeline-event {
491
+ position: relative;
492
+ margin-bottom: 20px;
493
+ display: flex;
494
+ flex-direction: column;
495
+ gap: 4px;
496
+ }
497
+
498
+ .timeline-event::before {
499
+ content: '';
500
+ position: absolute;
501
+ left: -20px;
502
+ top: 4px;
503
+ width: 10px;
504
+ height: 10px;
505
+ border-radius: 50%;
506
+ background-color: var(--bg-tertiary);
507
+ border: 2px solid var(--border-color);
508
+ }
509
+
510
+ .timeline-event.active::before {
511
+ background-color: var(--accent-primary);
512
+ border-color: #818cf8;
513
+ box-shadow: 0 0 8px rgba(99, 102, 241, 0.6);
514
+ }
515
+
516
+ .timeline-event.completed::before {
517
+ background-color: var(--success);
518
+ border-color: #34d399;
519
+ }
520
+
521
+ .timeline-event.failed::before {
522
+ background-color: var(--danger);
523
+ border-color: #f87171;
524
+ }
525
+
526
+ .event-header {
527
+ display: flex;
528
+ align-items: center;
529
+ justify-content: space-between;
530
+ }
531
+
532
+ .event-name {
533
+ font-size: 13px;
534
+ font-weight: 600;
535
+ }
536
+
537
+ .event-time {
538
+ font-size: 11px;
539
+ color: var(--text-muted);
540
+ font-family: 'JetBrains Mono', monospace;
541
+ }
542
+
543
+ .event-desc {
544
+ font-size: 12px;
545
+ color: var(--text-secondary);
546
+ line-height: 1.4;
547
+ }
548
+
549
+ /* Tabs Styling */
550
+ .details-tabs {
551
+ display: flex;
552
+ border-bottom: 1px solid var(--border-color);
553
+ background-color: rgba(17, 24, 39, 0.4);
554
+ padding: 0 24px;
555
+ }
556
+
557
+ .tab-btn {
558
+ padding: 14px 16px;
559
+ font-size: 13px;
560
+ font-weight: 500;
561
+ color: var(--text-secondary);
562
+ background: none;
563
+ border: none;
564
+ border-bottom: 2px solid transparent;
565
+ cursor: pointer;
566
+ transition: all 0.2s;
567
+ }
568
+
569
+ .tab-btn.active {
570
+ color: var(--accent-primary);
571
+ border-bottom-color: var(--accent-primary);
572
+ }
573
+
574
+ .tab-content {
575
+ display: none;
576
+ padding: 20px;
577
+ background-color: var(--bg-primary);
578
+ border: 1px solid var(--border-color);
579
+ border-radius: 8px;
580
+ }
581
+
582
+ .tab-content.active {
583
+ display: block;
584
+ }
585
+
586
+ /* Output Viewports */
587
+ .code-block {
588
+ font-family: 'JetBrains Mono', monospace;
589
+ font-size: 12.5px;
590
+ line-height: 1.6;
591
+ background-color: #05070c;
592
+ border: 1px solid var(--border-color);
593
+ border-radius: 6px;
594
+ padding: 16px;
595
+ overflow-x: auto;
596
+ color: #cbd5e1;
597
+ }
598
+
599
+ .result-markdown {
600
+ line-height: 1.6;
601
+ color: #cbd5e1;
602
+ font-size: 13.5px;
603
+ }
604
+
605
+ .result-markdown h1, .result-markdown h2, .result-markdown h3 {
606
+ color: white;
607
+ margin-top: 20px;
608
+ margin-bottom: 8px;
609
+ font-weight: 600;
610
+ }
611
+
612
+ .result-markdown h1 { font-size: 1.4em; border-bottom: 1px solid var(--border-color); padding-bottom: 6px; }
613
+ .result-markdown h2 { font-size: 1.2em; }
614
+ .result-markdown p { margin-bottom: 12px; }
615
+ .result-markdown code {
616
+ font-family: 'JetBrains Mono', monospace;
617
+ background-color: var(--bg-tertiary);
618
+ padding: 2px 6px;
619
+ border-radius: 4px;
620
+ color: #fb7185;
621
+ font-size: 0.9em;
622
+ }
623
+
624
+ .result-markdown pre {
625
+ background-color: #05070c;
626
+ border: 1px solid var(--border-color);
627
+ padding: 14px;
628
+ border-radius: 6px;
629
+ margin: 12px 0;
630
+ overflow-x: auto;
631
+ }
632
+
633
+ .result-markdown pre code {
634
+ background: none;
635
+ color: inherit;
636
+ padding: 0;
637
+ }
638
+
639
+ .progress-indicator {
640
+ display: flex;
641
+ align-items: center;
642
+ justify-content: space-between;
643
+ font-size: 12px;
644
+ font-weight: 500;
645
+ color: var(--text-secondary);
646
+ }
647
+
648
+ .progress-bar-glow {
649
+ height: 6px;
650
+ background-color: var(--bg-tertiary);
651
+ border-radius: 3px;
652
+ overflow: hidden;
653
+ box-shadow: inset 0 1px 2px rgba(0,0,0,0.4);
654
+ }
655
+
656
+ .progress-fill {
657
+ height: 100%;
658
+ background: linear-gradient(90deg, var(--accent-primary), var(--success));
659
+ width: 0%;
660
+ transition: width 0.3s ease;
661
+ box-shadow: 0 0 8px rgba(99, 102, 241, 0.5);
662
+ }
663
+ </style>
664
+ </head>
665
+ <body>
666
+
667
+ <!-- Left Sidebar: Form & Namespace Branding -->
668
+ <div class="sidebar">
669
+ <div class="brand-header">
670
+ <div class="brand-logo">Q</div>
671
+ <span class="brand-title">AgyQueue</span>
672
+ <span class="brand-badge">Console</span>
673
+ </div>
674
+
675
+ <div class="sidebar-section">Namespace</div>
676
+ <div style="padding: 0 20px 20px; border-bottom: 1px solid var(--border-color);">
677
+ <select style="width: 100%; background-color: var(--bg-primary); border: 1px solid var(--border-color); color: white; padding: 8px; border-radius: 6px; font-size: 12px; outline: none;">
678
+ <option value="default">default-namespace</option>
679
+ <option value="production">production-namespace</option>
680
+ </select>
681
+ </div>
682
+
683
+ <div class="sidebar-section">Start Workflow</div>
684
+ <form class="task-submitter-box" id="workflowForm">
685
+ <div class="form-group">
686
+ <label for="promptInput">Task/Prompt payload</label>
687
+ <textarea id="promptInput" rows="4" required placeholder="Describe task instructions..."></textarea>
688
+ </div>
689
+ <div class="form-group">
690
+ <label for="typeSelect">Workflow Executor</label>
691
+ <select id="typeSelect">
692
+ <option value="generic">generic_runner</option>
693
+ <option value="manifest_compliance">manifest_compliance</option>
694
+ <option value="fastapi_gen">fastapi_gen</option>
695
+ <option value="multi_agent">multi_agent_orchestrator</option>
696
+ </select>
697
+ </div>
698
+ <button type="submit" class="btn">
699
+ <svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
700
+ <path stroke-linecap="round" stroke-linejoin="round" d="M5.25 5.653c0-.856.917-1.398 1.667-.986l11.54 6.348a1.125 1.125 0 010 1.971l-11.54 6.347a1.125 1.125 0 01-1.667-.985V5.653z" />
701
+ </svg>
702
+ Run Workflow
703
+ </button>
704
+ </form>
705
+ </div>
706
+
707
+ <!-- Main Workspace Area -->
708
+ <div class="workspace">
709
+ <!-- Header -->
710
+ <div class="console-header">
711
+ <div class="console-path">
712
+ Workflows / <span class="active">runs</span>
713
+ </div>
714
+ <div style="font-size: 12px; color: var(--text-muted); font-family: 'JetBrains Mono', monospace;" id="liveClock">
715
+ UTC: --:--:--
716
+ </div>
717
+ </div>
718
+
719
+ <!-- Grid Filter / Actions Bar -->
720
+ <div class="grid-filters">
721
+ <div class="search-wrapper">
722
+ <svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
723
+ <path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
724
+ </svg>
725
+ <input type="text" class="search-input" id="searchInput" placeholder="Search by Workflow Run ID..." oninput="filterGrid()">
726
+ </div>
727
+
728
+ <div class="status-tabs" id="statusTabs">
729
+ <div class="status-tab active" data-status="all" onclick="filterStatus('all')">All <span class="status-count" id="count-all">0</span></div>
730
+ <div class="status-tab" data-status="running" onclick="filterStatus('running')">Running <span class="status-count" id="count-running">0</span></div>
731
+ <div class="status-tab" data-status="completed" onclick="filterStatus('completed')">Completed <span class="status-count" id="count-completed">0</span></div>
732
+ <div class="status-tab" data-status="failed" onclick="filterStatus('failed')">Failed <span class="status-count" id="count-failed">0</span></div>
733
+ </div>
734
+ </div>
735
+
736
+ <!-- Panel Grid & Details -->
737
+ <div class="content-panel">
738
+ <!-- Workflows Grid Table -->
739
+ <div class="workflows-grid-container">
740
+ <table class="workflow-table">
741
+ <thead>
742
+ <tr>
743
+ <th>Workflow Run ID</th>
744
+ <th>Type</th>
745
+ <th>Status</th>
746
+ <th>Progress</th>
747
+ <th>Start Time</th>
748
+ </tr>
749
+ </thead>
750
+ <tbody id="workflowGridBody">
751
+ <!-- Dynamic grid rows -->
752
+ </tbody>
753
+ </table>
754
+ </div>
755
+
756
+ <!-- Side Details Pane -->
757
+ <div class="details-pane" id="detailsPane">
758
+ <div class="details-pane-placeholder" id="detailsPlaceholder">
759
+ <svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
760
+ <path stroke-linecap="round" stroke-linejoin="round" d="M9 12h3.75M9 15h3.375c.9 0 1.625-.725 1.625-1.625V11.25M9 12V9c0-.9.725-1.625 1.625-1.625h2.75C14.275 7.375 15 8.1 15 9v2.25m-6 3h3.75m-3.75 3h.008v.008H12v-.008zm1.5-6h.008v.008H13.5V9.75zm1.5 6h.008v.008H15v-.008zm1.5-3h.008v.008H16.5v-.008z" />
761
+ </svg>
762
+ <p>Select a workflow run from the grid to inspect event history, payloads, and execution logs.</p>
763
+ </div>
764
+
765
+ <div id="detailsWorkspace" style="display: none; height: 100%; flex-direction: column;">
766
+ <!-- Header -->
767
+ <div class="details-header">
768
+ <div class="details-title-row">
769
+ <span class="details-workflow-id" id="detWorkflowId">agy-xxxx</span>
770
+ <div class="details-actions">
771
+ <button class="btn btn-secondary btn-danger" id="detCancelBtn" style="padding: 6px 12px; font-size: 11px;">Cancel Run</button>
772
+ </div>
773
+ </div>
774
+
775
+ <div class="details-meta-grid">
776
+ <div class="meta-item">
777
+ <span class="meta-label">Executor Type</span>
778
+ <span class="meta-value" id="detMetaType">generic</span>
779
+ </div>
780
+ <div class="meta-item">
781
+ <span class="meta-label">Status</span>
782
+ <div><span class="status-badge" id="detMetaBadge">COMPLETED</span></div>
783
+ </div>
784
+ <div class="meta-item">
785
+ <span class="meta-label">Parent Task</span>
786
+ <span class="meta-value" id="detMetaParent">None</span>
787
+ </div>
788
+ <div class="meta-item">
789
+ <span class="meta-label">Start Time</span>
790
+ <span class="meta-value" id="detMetaStart">--/-- --:--</span>
791
+ </div>
792
+ </div>
793
+ </div>
794
+
795
+ <!-- Tabs Selector -->
796
+ <div class="details-tabs">
797
+ <button class="tab-btn active" onclick="switchTab('history')">History Events</button>
798
+ <button class="tab-btn" onclick="switchTab('result')">Result Output</button>
799
+ <button class="tab-btn" onclick="switchTab('payload')">JSON Payload</button>
800
+ </div>
801
+
802
+ <!-- Details Body Content -->
803
+ <div class="details-body">
804
+ <!-- History Timeline Tab -->
805
+ <div class="tab-content active" id="tab-history">
806
+ <div class="history-timeline" id="detTimeline">
807
+ <!-- Dynamic timeline nodes -->
808
+ </div>
809
+ </div>
810
+
811
+ <!-- Result Output Tab -->
812
+ <div class="tab-content" id="tab-result">
813
+ <div class="result-markdown" id="detResult">
814
+ <!-- Markdown content -->
815
+ </div>
816
+ </div>
817
+
818
+ <!-- Raw JSON Payload Tab -->
819
+ <div class="tab-content" id="tab-payload">
820
+ <pre class="code-block" id="detPayload">{}</pre>
821
+ </div>
822
+ </div>
823
+ </div>
824
+ </div>
825
+ </div>
826
+ </div>
827
+
828
+ <script>
829
+ let currentTaskId = null;
830
+ let pollIntervalId = null;
831
+ let allTasks = [];
832
+ let selectedStatusFilter = 'all';
833
+
834
+ // Update live clock
835
+ setInterval(() => {
836
+ document.getElementById('liveClock').innerText = 'UTC: ' + new Date().toISOString().replace('T', ' ').substring(0, 19);
837
+ }, 1000);
838
+
839
+ // Fetch task list and reload grid
840
+ async function fetchTaskList() {
841
+ try {
842
+ const response = await fetch('/api/tasks');
843
+ allTasks = await response.json();
844
+
845
+ // Update filter badge counts
846
+ updateCounts();
847
+
848
+ // Render table based on current filters
849
+ renderGrid();
850
+ } catch (err) {
851
+ console.error("Error fetching tasks:", err);
852
+ }
853
+ }
854
+
855
+ // Calculate and update dashboard badge counts
856
+ function updateCounts() {
857
+ document.getElementById('count-all').innerText = allTasks.length;
858
+ document.getElementById('count-running').innerText = allTasks.filter(t => ['RUNNING', 'WAITING'].includes(t.status)).length;
859
+ document.getElementById('count-completed').innerText = allTasks.filter(t => t.status === 'COMPLETED').length;
860
+ document.getElementById('count-failed').innerText = allTasks.filter(t => t.status === 'FAILED').length;
861
+ }
862
+
863
+ // Render rows based on filter state
864
+ function renderGrid() {
865
+ const searchVal = document.getElementById('searchInput').value.toLowerCase();
866
+ const body = document.getElementById('workflowGridBody');
867
+ body.innerHTML = '';
868
+
869
+ const filtered = allTasks.filter(task => {
870
+ // Search filter
871
+ const matchesSearch = task.task_id.toLowerCase().includes(searchVal);
872
+ if (!matchesSearch) return false;
873
+
874
+ // Status tab filter
875
+ if (selectedStatusFilter === 'all') return true;
876
+ if (selectedStatusFilter === 'running') return ['RUNNING', 'WAITING', 'QUEUED'].includes(task.status);
877
+ return task.status.toLowerCase() === selectedStatusFilter;
878
+ });
879
+
880
+ if (filtered.length === 0) {
881
+ body.innerHTML = `<tr><td colspan="5" style="text-align: center; color: var(--text-muted); padding: 30px;">No matching workflows found.</td></tr>`;
882
+ return;
883
+ }
884
+
885
+ filtered.forEach(task => {
886
+ const isSelected = task.task_id === currentTaskId ? 'selected' : '';
887
+ const progress = task.progress || 0;
888
+ const formattedTime = task.created_at ? task.created_at.substring(11, 19) : '--:--:--';
889
+
890
+ const row = document.createElement('tr');
891
+ row.className = `workflow-row ${isSelected}`;
892
+ row.onclick = () => selectWorkflow(task.task_id);
893
+ row.innerHTML = `
894
+ <td class="workflow-id-col">${task.task_id}</td>
895
+ <td><span class="workflow-type-badge">${task.task_type}</span></td>
896
+ <td><span class="status-badge status-badge-${task.status.toLowerCase()}">${task.status}</span></td>
897
+ <td style="width: 160px;">
898
+ <div class="progress-indicator" style="margin-bottom: 4px;">
899
+ <span>Progress</span>
900
+ <span>${progress}%</span>
901
+ </div>
902
+ <div class="progress-bar-glow">
903
+ <div class="progress-fill" style="width: ${progress}%"></div>
904
+ </div>
905
+ </td>
906
+ <td style="color: var(--text-secondary); font-family: 'JetBrains Mono', monospace;">${formattedTime}</td>
907
+ `;
908
+ body.appendChild(row);
909
+ });
910
+ }
911
+
912
+ // Handle navigation to workflow selection
913
+ async function selectWorkflow(taskId) {
914
+ currentTaskId = taskId;
915
+
916
+ // Re-render table rows to update highlight
917
+ renderGrid();
918
+
919
+ document.getElementById('detailsPlaceholder').style.display = 'none';
920
+ document.getElementById('detailsWorkspace').style.display = 'flex';
921
+
922
+ await refreshWorkflowDetails();
923
+
924
+ // Set details refreshing
925
+ if (pollIntervalId) clearInterval(pollIntervalId);
926
+ pollIntervalId = setInterval(refreshWorkflowDetails, 1500);
927
+ }
928
+
929
+ // Fetch detailed task state from endpoint
930
+ async function refreshWorkflowDetails() {
931
+ if (!currentTaskId) return;
932
+
933
+ try {
934
+ const response = await fetch(`/api/tasks/${currentTaskId}`);
935
+ if (!response.ok) {
936
+ if (response.status === 404) {
937
+ alert("Task not found");
938
+ clearInterval(pollIntervalId);
939
+ location.reload();
940
+ }
941
+ return;
942
+ }
943
+ const task = await response.json();
944
+
945
+ document.getElementById('detWorkflowId').innerText = task.task_id;
946
+ document.getElementById('detMetaType').innerText = task.task_type;
947
+
948
+ const badge = document.getElementById('detMetaBadge');
949
+ badge.innerText = task.status;
950
+ badge.className = `status-badge status-badge-${task.status.toLowerCase()}`;
951
+
952
+ // Parent Task ID navigation
953
+ const parentCell = document.getElementById('detMetaParent');
954
+ if (task.parent_id) {
955
+ parentCell.innerText = task.parent_id;
956
+ parentCell.className = "meta-value meta-value-mono";
957
+ parentCell.onclick = () => selectWorkflow(task.parent_id);
958
+ } else {
959
+ parentCell.innerText = "None";
960
+ parentCell.className = "meta-value";
961
+ parentCell.onclick = null;
962
+ }
963
+
964
+ document.getElementById('detMetaStart').innerText = task.updated_at ? task.updated_at.replace('T', ' ').substring(0, 19) : '--/-- --:--';
965
+
966
+ // Show/hide cancel button
967
+ const cancelBtn = document.getElementById('detCancelBtn');
968
+ if (['QUEUED', 'RUNNING', 'WAITING'].includes(task.status)) {
969
+ cancelBtn.style.display = 'block';
970
+ cancelBtn.onclick = () => cancelWorkflow(task.task_id);
971
+ } else {
972
+ cancelBtn.style.display = 'none';
973
+ }
974
+
975
+ // Update raw payload view
976
+ document.getElementById('detPayload').innerText = JSON.stringify(task, null, 2);
977
+
978
+ // Update result report
979
+ const resultTab = document.getElementById('detResult');
980
+ if (task.status === 'COMPLETED' && task.result) {
981
+ resultTab.innerHTML = renderMarkdown(task.result);
982
+ } else if (task.status === 'FAILED' && task.error) {
983
+ resultTab.innerHTML = `<h3 style="color: var(--danger);">Execution Failed</h3><pre class="code-block" style="margin-top: 10px; border-color: rgba(239, 68, 68, 0.4);">${task.error}</pre>`;
984
+ } else if (task.status === 'CANCELLED') {
985
+ resultTab.innerHTML = `<h3 style="color: var(--text-secondary);">Workflow Aborted</h3><p style="margin-top: 8px;">Cancelled by client command. ${task.error || ''}</p>`;
986
+ } else {
987
+ resultTab.innerHTML = `<p style="color: var(--text-muted);">Workflow is still running. Reports are available upon completion.</p>`;
988
+ }
989
+
990
+ // Build history event log to show execution milestones
991
+ buildTimelineEvents(task);
992
+
993
+ } catch (err) {
994
+ console.error("Error refreshing task details:", err);
995
+ }
996
+ }
997
+
998
+ // Render vertical timeline history events list
999
+ function buildTimelineEvents(task) {
1000
+ const timeline = document.getElementById('detTimeline');
1001
+ timeline.innerHTML = '';
1002
+
1003
+ // Event 1: Workflow Started
1004
+ const ev1 = createTimelineNode("WorkflowExecutionStarted", task.updated_at, "Task enqueued inside the AgyQueue task broker. Status transitioned to QUEUED.", "completed");
1005
+ timeline.appendChild(ev1);
1006
+
1007
+ // Event 2: Worker Picked Up (if running or completed)
1008
+ if (task.status !== 'QUEUED') {
1009
+ const ev2 = createTimelineNode("WorkflowTaskStarted", task.updated_at, "Background worker picked up payload and initialized isolation workspace worktree directory.", "completed");
1010
+ timeline.appendChild(ev2);
1011
+ }
1012
+
1013
+ // Event 3: Workflow Step updates
1014
+ if (task.step && task.step !== "Queued in AgyQueue") {
1015
+ const isFinal = ['COMPLETED', 'FAILED', 'CANCELLED'].includes(task.status);
1016
+ const ev3 = createTimelineNode("WorkflowTaskScheduled", task.updated_at, `Current executing task step log: "${task.step}" (Progress: ${task.progress}%)`, isFinal ? "completed" : "active");
1017
+ timeline.appendChild(ev3);
1018
+ }
1019
+
1020
+ // Event 4: Terminal execution event
1021
+ if (task.status === 'COMPLETED') {
1022
+ const ev4 = createTimelineNode("WorkflowExecutionCompleted", task.updated_at, "Task finished execution successfully. Results aggregated and saved to persistent store.", "completed");
1023
+ timeline.appendChild(ev4);
1024
+ } else if (task.status === 'FAILED') {
1025
+ const ev4 = createTimelineNode("WorkflowExecutionFailed", task.updated_at, `Execution crashed. Error message: "${task.error || 'Unknown execution error'}"`, "failed");
1026
+ timeline.appendChild(ev4);
1027
+ } else if (task.status === 'CANCELLED') {
1028
+ const ev4 = createTimelineNode("WorkflowExecutionCancelled", task.updated_at, "Workflow execution terminated cleanly via API cancellation signal.", "failed");
1029
+ timeline.appendChild(ev4);
1030
+ }
1031
+ }
1032
+
1033
+ function createTimelineNode(name, time, description, stateClass) {
1034
+ const node = document.createElement('div');
1035
+ node.className = `timeline-event ${stateClass}`;
1036
+ node.innerHTML = `
1037
+ <div class="event-header">
1038
+ <span class="event-name">${name}</span>
1039
+ <span class="event-time">${time ? time.substring(11, 19) : ''}</span>
1040
+ </div>
1041
+ <div class="event-desc">${description}</div>
1042
+ `;
1043
+ return node;
1044
+ }
1045
+
1046
+ // Cancel workflow request
1047
+ async function cancelWorkflow(taskId) {
1048
+ if (!confirm(`Abort workflow execution run ${taskId}?`)) return;
1049
+ try {
1050
+ const response = await fetch(`/api/tasks/${taskId}/cancel`, { method: 'POST' });
1051
+ if (response.ok) {
1052
+ await refreshWorkflowDetails();
1053
+ await fetchTaskList();
1054
+ } else {
1055
+ alert("Failed to abort workflow");
1056
+ }
1057
+ } catch (err) {
1058
+ console.error("Error cancelling workflow:", err);
1059
+ }
1060
+ }
1061
+
1062
+ // Form Submit
1063
+ document.getElementById('workflowForm').onsubmit = async (e) => {
1064
+ e.preventDefault();
1065
+ const prompt = document.getElementById('promptInput').value;
1066
+ const task_type = document.getElementById('typeSelect').value;
1067
+
1068
+ try {
1069
+ const response = await fetch('/api/tasks', {
1070
+ method: 'POST',
1071
+ headers: { 'Content-Type': 'application/json' },
1072
+ body: JSON.stringify({ prompt, task_type })
1073
+ });
1074
+
1075
+ if (response.ok) {
1076
+ const task = await response.json();
1077
+ document.getElementById('promptInput').value = '';
1078
+ await fetchTaskList();
1079
+ selectWorkflow(task.task_id);
1080
+ } else {
1081
+ alert("Failed to submit task");
1082
+ }
1083
+ } catch (err) {
1084
+ console.error("Error submitting task:", err);
1085
+ }
1086
+ };
1087
+
1088
+ // Filter by Status Tab
1089
+ function filterStatus(status) {
1090
+ selectedStatusFilter = status;
1091
+
1092
+ const tabs = document.querySelectorAll('.status-tab');
1093
+ tabs.forEach(tab => {
1094
+ if (tab.getAttribute('data-status') === status) {
1095
+ tab.classList.add('active');
1096
+ } else {
1097
+ tab.classList.remove('active');
1098
+ }
1099
+ });
1100
+
1101
+ renderGrid();
1102
+ }
1103
+
1104
+ // Search text filter
1105
+ function filterGrid() {
1106
+ renderGrid();
1107
+ }
1108
+
1109
+ // Tab selection switching
1110
+ function switchTab(tabId) {
1111
+ const tabs = document.querySelectorAll('.tab-btn');
1112
+ tabs.forEach(btn => {
1113
+ if (btn.innerText.toLowerCase().includes(tabId)) {
1114
+ btn.classList.add('active');
1115
+ } else {
1116
+ btn.classList.remove('active');
1117
+ }
1118
+ });
1119
+
1120
+ const contents = document.querySelectorAll('.tab-content');
1121
+ contents.forEach(content => {
1122
+ if (content.id === `tab-${tabId}`) {
1123
+ content.classList.add('active');
1124
+ } else {
1125
+ content.classList.remove('active');
1126
+ }
1127
+ });
1128
+ }
1129
+
1130
+ // Render basic markdown formatting
1131
+ function renderMarkdown(md) {
1132
+ if (!md) return "";
1133
+ return md
1134
+ .replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") // escape HTML
1135
+ .replace(/^# (.*$)/gim, '<h1>$1</h1>')
1136
+ .replace(/^## (.*$)/gim, '<h2>$1</h2>')
1137
+ .replace(/^### (.*$)/gim, '<h3>$1</h3>')
1138
+ .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
1139
+ .replace(/\*(.*?)\*/g, '<em>$1</em>')
1140
+ .replace(/`([^`]+)`/g, '<code>$1</code>')
1141
+ .replace(/- \[(x| )\] (.*$)/gim, (m, check, text) => {
1142
+ const checked = check === 'x' ? 'checked disabled' : 'disabled';
1143
+ return `<div><input type="checkbox" ${checked}> ${text}</div>`;
1144
+ })
1145
+ .replace(/^- (.*$)/gim, '<li>$1</li>')
1146
+ .replace(/\n\n/g, '<p></p>')
1147
+ .replace(/\n/g, '<br>');
1148
+ }
1149
+
1150
+ // Initial trigger
1151
+ fetchTaskList();
1152
+ setInterval(fetchTaskList, 3000); // refresh list every 3s
1153
+ </script>
1154
+ </body>
1155
+ </html>