@agwab/pi-workflow 0.1.2 → 0.2.0
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.
- package/README.md +7 -13
- package/dist/compiler.d.ts +2 -0
- package/dist/compiler.js +27 -2
- package/dist/engine.d.ts +2 -0
- package/dist/engine.js +3 -2
- package/dist/extension.js +201 -16
- package/dist/store.js +1 -0
- package/dist/types.d.ts +3 -0
- package/dist/workflow-progress-health.d.ts +37 -0
- package/dist/workflow-progress-health.js +296 -0
- package/dist/workflow-runtime.d.ts +6 -0
- package/dist/workflow-runtime.js +33 -10
- package/dist/workflow-view.d.ts +2 -0
- package/dist/workflow-view.js +97 -18
- package/dist/workflow-web-source.js +32 -14
- package/docs/usage.md +1 -1
- package/package.json +6 -6
- package/src/compiler.ts +41 -2
- package/src/engine.ts +7 -16
- package/src/extension.ts +254 -22
- package/src/store.ts +1 -0
- package/src/types.ts +4 -0
- package/src/workflow-progress-health.ts +461 -0
- package/src/workflow-runtime.ts +50 -13
- package/src/workflow-view.ts +186 -41
- package/src/workflow-web-source.ts +192 -69
- package/workflows/deep-research/helpers/claim-evidence-gate.mjs +111 -37
- package/workflows/deep-research/helpers/final-audit-packet.mjs +191 -14
- package/workflows/deep-research/helpers/normalize-input-packet.mjs +159 -50
- package/workflows/deep-research/helpers/render-executive.mjs +671 -37
- package/workflows/deep-research/helpers/sanitize-verification-candidates.mjs +624 -0
- package/workflows/deep-research/schemas/deep-research-executive-render-control.schema.json +2 -0
- package/workflows/deep-research/schemas/deep-research-final-synthesis-control.schema.json +110 -0
- package/workflows/deep-research/spec.json +41 -11
package/src/workflow-view.ts
CHANGED
|
@@ -9,6 +9,11 @@ import {
|
|
|
9
9
|
readIndex,
|
|
10
10
|
readRunRecord,
|
|
11
11
|
} from "./store.js";
|
|
12
|
+
import {
|
|
13
|
+
diagnoseWorkflowRunHealth,
|
|
14
|
+
diagnoseWorkflowTaskHealth,
|
|
15
|
+
type WorkflowProgressHealth,
|
|
16
|
+
} from "./workflow-progress-health.js";
|
|
12
17
|
import {
|
|
13
18
|
type WorkflowIndexRecord,
|
|
14
19
|
type WorkflowRunRecord,
|
|
@@ -341,7 +346,8 @@ export class WorkflowView implements Component {
|
|
|
341
346
|
}
|
|
342
347
|
|
|
343
348
|
private renderDrilldownHeader(width: number): string[] {
|
|
344
|
-
const taskSummary =
|
|
349
|
+
const taskSummary =
|
|
350
|
+
this.mode !== "runs" ? this.detailRun?.taskSummary : undefined;
|
|
345
351
|
const active = taskSummary
|
|
346
352
|
? taskSummary.running
|
|
347
353
|
: this.flows.filter((flow) => flow.status === "running").length;
|
|
@@ -371,6 +377,10 @@ export class WorkflowView implements Component {
|
|
|
371
377
|
|
|
372
378
|
private renderRunsScreen(width: number): string[] {
|
|
373
379
|
const selected = this.flows[this.selectedFlow];
|
|
380
|
+
const selectedDetail =
|
|
381
|
+
selected && this.detailRun?.runId === selected.runId
|
|
382
|
+
? this.detailRun
|
|
383
|
+
: undefined;
|
|
374
384
|
const sideLines = [
|
|
375
385
|
accent(this.theme, "All runs"),
|
|
376
386
|
kvRow(this.theme, "total", String(this.flows.length)),
|
|
@@ -379,10 +389,19 @@ export class WorkflowView implements Component {
|
|
|
379
389
|
"running",
|
|
380
390
|
String(this.flows.filter((flow) => flow.status === "running").length),
|
|
381
391
|
),
|
|
392
|
+
kvRow(
|
|
393
|
+
this.theme,
|
|
394
|
+
"needs action",
|
|
395
|
+
String(
|
|
396
|
+
this.flows.filter((flow) =>
|
|
397
|
+
["failed", "blocked", "interrupted"].includes(flow.status),
|
|
398
|
+
).length,
|
|
399
|
+
),
|
|
400
|
+
),
|
|
382
401
|
"",
|
|
383
402
|
accent(this.theme, "Selected"),
|
|
384
403
|
...(selected
|
|
385
|
-
? this.runSummaryLines(selected)
|
|
404
|
+
? this.runSummaryLines(selected, selectedDetail)
|
|
386
405
|
: [placeholder(this.theme, "none")]),
|
|
387
406
|
];
|
|
388
407
|
return this.renderTwoPane(
|
|
@@ -480,13 +499,14 @@ export class WorkflowView implements Component {
|
|
|
480
499
|
run: WorkflowRunRecord,
|
|
481
500
|
task: WorkflowTaskRunRecord,
|
|
482
501
|
): string[] {
|
|
502
|
+
const taskHealth = diagnoseWorkflowTaskHealth(task, run);
|
|
483
503
|
const lines = [
|
|
484
504
|
...boxed(
|
|
485
505
|
this.theme,
|
|
486
506
|
"Task Detail",
|
|
487
507
|
width,
|
|
488
508
|
[
|
|
489
|
-
`${statusGlyph(this.theme, task.status)} ${strong(this.theme, task.displayName)} ${statusBadge(this.theme, task.status)} ${muted(this.theme, this.breadcrumbText())}`,
|
|
509
|
+
`${statusGlyph(this.theme, task.status)} ${strong(this.theme, task.displayName)} ${statusBadge(this.theme, task.status)} ${healthInline(this.theme, taskHealth)} ${muted(this.theme, this.breadcrumbText())}`,
|
|
490
510
|
taskMetaLine(this.theme, [
|
|
491
511
|
["agent", task.agent],
|
|
492
512
|
["stage", task.stageId ?? "(none)"],
|
|
@@ -499,6 +519,20 @@ export class WorkflowView implements Component {
|
|
|
499
519
|
"",
|
|
500
520
|
];
|
|
501
521
|
|
|
522
|
+
const healthLines = this.taskHealthLines(taskHealth, width - 4);
|
|
523
|
+
if (healthLines.length > 0) {
|
|
524
|
+
lines.push(
|
|
525
|
+
...boxed(
|
|
526
|
+
this.theme,
|
|
527
|
+
"Health",
|
|
528
|
+
width,
|
|
529
|
+
healthLines,
|
|
530
|
+
healthColor(taskHealth),
|
|
531
|
+
),
|
|
532
|
+
"",
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
|
|
502
536
|
const validationLines = this.taskValidationStripLines(task, width - 4);
|
|
503
537
|
if (validationLines.length > 0) {
|
|
504
538
|
lines.push(
|
|
@@ -578,7 +612,9 @@ export class WorkflowView implements Component {
|
|
|
578
612
|
);
|
|
579
613
|
const statusWidth = Math.max(
|
|
580
614
|
7,
|
|
581
|
-
...window.rows.map(({ item }) =>
|
|
615
|
+
...window.rows.map(({ item }) =>
|
|
616
|
+
visibleWidth(statusLabelText(runStatusLabel(item))),
|
|
617
|
+
),
|
|
582
618
|
);
|
|
583
619
|
for (const { item: flow, index } of window.rows) {
|
|
584
620
|
const selected = index === this.selectedFlow;
|
|
@@ -587,21 +623,19 @@ export class WorkflowView implements Component {
|
|
|
587
623
|
const name = flow.name ?? flow.type;
|
|
588
624
|
const left = `${prefix}${marker} ${selected ? strong(this.theme, name) : name}`;
|
|
589
625
|
const runIdText = shortId(flow.runId).slice(0, 16).padEnd(16, " ");
|
|
590
|
-
const
|
|
591
|
-
flow.
|
|
592
|
-
|
|
593
|
-
|
|
626
|
+
const detailRun =
|
|
627
|
+
this.detailRun?.runId === flow.runId ? this.detailRun : undefined;
|
|
628
|
+
const health = diagnoseWorkflowRunHealth(detailRun ?? flow);
|
|
629
|
+
const healthText =
|
|
630
|
+
health.state === "completed"
|
|
631
|
+
? ""
|
|
632
|
+
: ` ${muted(this.theme, "·")} ${healthLabel(this.theme, health)}`;
|
|
594
633
|
const baseRight = `${statusColumn(this.theme, flow.status, runStatusLabel(flow), statusWidth)} ${progressBar(this.theme, flow.taskSummary, 5)} ${metaValue(this.theme, runIdText)}`;
|
|
595
634
|
const right =
|
|
596
635
|
width >= 90
|
|
597
|
-
? `${baseRight} ${muted(this.theme, "·")} ${metaLabel(this.theme, "start")} ${metaValue(this.theme, timestampText(flow.createdAt))}${
|
|
598
|
-
: `${baseRight}${
|
|
599
|
-
const line = joinColumns(
|
|
600
|
-
left,
|
|
601
|
-
right,
|
|
602
|
-
width,
|
|
603
|
-
17,
|
|
604
|
-
);
|
|
636
|
+
? `${baseRight} ${muted(this.theme, "·")} ${metaLabel(this.theme, "start")} ${metaValue(this.theme, timestampText(flow.createdAt))}${healthText}`
|
|
637
|
+
: `${baseRight}${healthText}`;
|
|
638
|
+
const line = joinColumns(left, right, width, 17);
|
|
605
639
|
lines.push(selectedLine(this.theme, line, width, selected, true));
|
|
606
640
|
}
|
|
607
641
|
if (window.hiddenAfter > 0)
|
|
@@ -667,7 +701,11 @@ export class WorkflowView implements Component {
|
|
|
667
701
|
const selected = index === this.selectedTask;
|
|
668
702
|
const prefix = selected ? accent(this.theme, "› ") : " ";
|
|
669
703
|
const left = `${prefix}${statusGlyph(this.theme, task.status)} ${selected ? strong(this.theme, task.displayName) : task.displayName}`;
|
|
670
|
-
const right = taskListStatusLabel(
|
|
704
|
+
const right = taskListStatusLabel(
|
|
705
|
+
this.theme,
|
|
706
|
+
task,
|
|
707
|
+
diagnoseWorkflowTaskHealth(task, run),
|
|
708
|
+
);
|
|
671
709
|
const line = joinColumns(
|
|
672
710
|
left,
|
|
673
711
|
metaByStatus(this.theme, task.status, right),
|
|
@@ -685,7 +723,6 @@ export class WorkflowView implements Component {
|
|
|
685
723
|
: [placeholder(this.theme, " no tasks in selected stage")];
|
|
686
724
|
}
|
|
687
725
|
|
|
688
|
-
|
|
689
726
|
private taskIdentityLines(
|
|
690
727
|
run: WorkflowRunRecord,
|
|
691
728
|
task: WorkflowTaskRunRecord,
|
|
@@ -762,6 +799,48 @@ export class WorkflowView implements Component {
|
|
|
762
799
|
return lines.map((line) => fit(line, width));
|
|
763
800
|
}
|
|
764
801
|
|
|
802
|
+
private taskHealthLines(
|
|
803
|
+
health: WorkflowProgressHealth,
|
|
804
|
+
width: number,
|
|
805
|
+
): string[] {
|
|
806
|
+
if (health.state === "completed" || health.state === "pending") return [];
|
|
807
|
+
const lines = [
|
|
808
|
+
`${healthGlyph(this.theme, health)} ${healthLabel(this.theme, health)} ${muted(this.theme, health.summary)}`,
|
|
809
|
+
kvRow(this.theme, "suggested", health.suggestion, healthColor(health)),
|
|
810
|
+
kvRow(this.theme, "why", health.reason),
|
|
811
|
+
];
|
|
812
|
+
if (health.currentTask?.elapsedMs !== undefined)
|
|
813
|
+
lines.splice(
|
|
814
|
+
1,
|
|
815
|
+
0,
|
|
816
|
+
kvRow(
|
|
817
|
+
this.theme,
|
|
818
|
+
"elapsed",
|
|
819
|
+
formatDuration(health.currentTask.elapsedMs),
|
|
820
|
+
),
|
|
821
|
+
);
|
|
822
|
+
if (health.durationClass)
|
|
823
|
+
lines.push(
|
|
824
|
+
kvRow(this.theme, "duration", `${health.durationClass} expected`),
|
|
825
|
+
);
|
|
826
|
+
if (health.heartbeatAgeMs !== undefined)
|
|
827
|
+
lines.push(
|
|
828
|
+
kvRow(
|
|
829
|
+
this.theme,
|
|
830
|
+
"heartbeat",
|
|
831
|
+
`${formatDuration(health.heartbeatAgeMs)} ago`,
|
|
832
|
+
),
|
|
833
|
+
);
|
|
834
|
+
if (health.lastActivityAgeMs !== undefined)
|
|
835
|
+
lines.push(
|
|
836
|
+
kvRow(
|
|
837
|
+
this.theme,
|
|
838
|
+
"activity",
|
|
839
|
+
`${formatDuration(health.lastActivityAgeMs)} ago`,
|
|
840
|
+
),
|
|
841
|
+
);
|
|
842
|
+
return lines.map((line) => fit(line, width));
|
|
843
|
+
}
|
|
765
844
|
|
|
766
845
|
private taskValidationStripLines(
|
|
767
846
|
task: WorkflowTaskRunRecord,
|
|
@@ -770,10 +849,7 @@ export class WorkflowView implements Component {
|
|
|
770
849
|
const summary = taskValidationSummary(task);
|
|
771
850
|
if (!summary) return [];
|
|
772
851
|
return [
|
|
773
|
-
fit(
|
|
774
|
-
validationLine(this.theme, summary.status, summary.message),
|
|
775
|
-
width,
|
|
776
|
-
),
|
|
852
|
+
fit(validationLine(this.theme, summary.status, summary.message), width),
|
|
777
853
|
];
|
|
778
854
|
}
|
|
779
855
|
|
|
@@ -790,9 +866,7 @@ export class WorkflowView implements Component {
|
|
|
790
866
|
const maxStart = Math.max(0, total - TASK_ARTIFACT_VIEW_LINES);
|
|
791
867
|
const start = Math.min(this.artifactScrollLine, maxStart);
|
|
792
868
|
const end =
|
|
793
|
-
total === 0
|
|
794
|
-
? 0
|
|
795
|
-
: Math.min(total, start + TASK_ARTIFACT_VIEW_LINES);
|
|
869
|
+
total === 0 ? 0 : Math.min(total, start + TASK_ARTIFACT_VIEW_LINES);
|
|
796
870
|
const visible =
|
|
797
871
|
total === 0
|
|
798
872
|
? [
|
|
@@ -862,7 +936,6 @@ export class WorkflowView implements Component {
|
|
|
862
936
|
this.artifactScrollLine = 0;
|
|
863
937
|
}
|
|
864
938
|
|
|
865
|
-
|
|
866
939
|
private moveModeSelection(delta: number): void {
|
|
867
940
|
if (this.mode === "runs") {
|
|
868
941
|
this.moveRun(delta);
|
|
@@ -1005,11 +1078,11 @@ export class WorkflowView implements Component {
|
|
|
1005
1078
|
|
|
1006
1079
|
private syncSelectedTaskId(tasks?: WorkflowTaskRunRecord[]): void {
|
|
1007
1080
|
const stageTasks =
|
|
1008
|
-
tasks ??
|
|
1081
|
+
tasks ??
|
|
1082
|
+
(this.detailRun ? this.tasksForSelectedStage(this.detailRun) : []);
|
|
1009
1083
|
this.selectedTaskId = stageTasks[this.selectedTask]?.taskId ?? "";
|
|
1010
1084
|
}
|
|
1011
1085
|
|
|
1012
|
-
|
|
1013
1086
|
private breadcrumbText(): string {
|
|
1014
1087
|
const parts = ["workflow"];
|
|
1015
1088
|
const flow = this.flows[this.selectedFlow];
|
|
@@ -1025,7 +1098,11 @@ export class WorkflowView implements Component {
|
|
|
1025
1098
|
return parts.join(" › ");
|
|
1026
1099
|
}
|
|
1027
1100
|
|
|
1028
|
-
private runSummaryLines(
|
|
1101
|
+
private runSummaryLines(
|
|
1102
|
+
flow: WorkflowSummary,
|
|
1103
|
+
detailRun?: WorkflowRunRecord,
|
|
1104
|
+
): string[] {
|
|
1105
|
+
const health = diagnoseWorkflowRunHealth(detailRun ?? flow);
|
|
1029
1106
|
return [
|
|
1030
1107
|
`${statusGlyph(this.theme, flow.status)} ${strong(this.theme, flow.name ?? flow.type)} ${statusBadge(this.theme, flow.status, runStatusLabel(flow))}`,
|
|
1031
1108
|
progressBar(this.theme, flow.taskSummary, 8),
|
|
@@ -1048,10 +1125,43 @@ export class WorkflowView implements Component {
|
|
|
1048
1125
|
),
|
|
1049
1126
|
],
|
|
1050
1127
|
]),
|
|
1128
|
+
...this.runHealthLines(health),
|
|
1051
1129
|
];
|
|
1052
1130
|
}
|
|
1053
1131
|
|
|
1132
|
+
private runHealthLines(health: WorkflowProgressHealth): string[] {
|
|
1133
|
+
if (health.state === "completed") return [];
|
|
1134
|
+
const lines = [
|
|
1135
|
+
"",
|
|
1136
|
+
accent(this.theme, "Health"),
|
|
1137
|
+
`${healthGlyph(this.theme, health)} ${healthLabel(this.theme, health)} ${muted(this.theme, health.summary)}`,
|
|
1138
|
+
];
|
|
1139
|
+
if (health.currentTask?.displayName)
|
|
1140
|
+
lines.push(kvRow(this.theme, "current", health.currentTask.displayName));
|
|
1141
|
+
if (health.lastActivityAgeMs !== undefined)
|
|
1142
|
+
lines.push(
|
|
1143
|
+
kvRow(
|
|
1144
|
+
this.theme,
|
|
1145
|
+
"activity",
|
|
1146
|
+
`${formatDuration(health.lastActivityAgeMs)} ago`,
|
|
1147
|
+
),
|
|
1148
|
+
);
|
|
1149
|
+
if (health.heartbeatAgeMs !== undefined)
|
|
1150
|
+
lines.push(
|
|
1151
|
+
kvRow(
|
|
1152
|
+
this.theme,
|
|
1153
|
+
"heartbeat",
|
|
1154
|
+
`${formatDuration(health.heartbeatAgeMs)} ago`,
|
|
1155
|
+
),
|
|
1156
|
+
);
|
|
1157
|
+
lines.push(
|
|
1158
|
+
kvRow(this.theme, "suggested", health.suggestion, healthColor(health)),
|
|
1159
|
+
);
|
|
1160
|
+
return lines;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1054
1163
|
private runDetailSummaryLines(run: WorkflowRunRecord): string[] {
|
|
1164
|
+
const health = diagnoseWorkflowRunHealth(run);
|
|
1055
1165
|
const lines = [
|
|
1056
1166
|
`${statusGlyph(this.theme, run.status)} ${strong(this.theme, run.name ?? run.type)} ${statusBadge(this.theme, run.status)}`,
|
|
1057
1167
|
progressBar(this.theme, run.taskSummary, 10),
|
|
@@ -1071,6 +1181,7 @@ export class WorkflowView implements Component {
|
|
|
1071
1181
|
["updated", timestampText(run.updatedAt)],
|
|
1072
1182
|
]),
|
|
1073
1183
|
kvRow(this.theme, "run", shortId(run.runId)),
|
|
1184
|
+
...this.runHealthLines(health),
|
|
1074
1185
|
];
|
|
1075
1186
|
if (run.fanout && run.fanout.length > 0) {
|
|
1076
1187
|
lines.push("", accent(this.theme, "Fanout"));
|
|
@@ -1197,7 +1308,6 @@ function runToSummary(cwd: string, run: WorkflowRunRecord): WorkflowSummary {
|
|
|
1197
1308
|
};
|
|
1198
1309
|
}
|
|
1199
1310
|
|
|
1200
|
-
|
|
1201
1311
|
async function readFileLinesBounded(
|
|
1202
1312
|
cwd: string,
|
|
1203
1313
|
projectPath: string | undefined,
|
|
@@ -1255,7 +1365,9 @@ function summarizeTasks(tasks: WorkflowTaskRunRecord[]): TaskSummary {
|
|
|
1255
1365
|
return summary;
|
|
1256
1366
|
}
|
|
1257
1367
|
|
|
1258
|
-
function statusForSummary(
|
|
1368
|
+
function statusForSummary(
|
|
1369
|
+
summary: TaskSummary,
|
|
1370
|
+
): WorkflowRunStatus | TaskRunStatus {
|
|
1259
1371
|
if (summary.running > 0) return "running";
|
|
1260
1372
|
if (summary.blocked > 0) return "blocked";
|
|
1261
1373
|
if (summary.failed > 0 || summary.interrupted > 0) return "failed";
|
|
@@ -1364,7 +1476,10 @@ function statusColumn(
|
|
|
1364
1476
|
width: number,
|
|
1365
1477
|
): string {
|
|
1366
1478
|
const normalized = statusLabelText(label);
|
|
1367
|
-
return padAnsi(
|
|
1479
|
+
return padAnsi(
|
|
1480
|
+
fg(theme, statusColor(status), strong(theme, normalized)),
|
|
1481
|
+
width,
|
|
1482
|
+
);
|
|
1368
1483
|
}
|
|
1369
1484
|
|
|
1370
1485
|
function statusLabelText(label: string): string {
|
|
@@ -1384,9 +1499,10 @@ function progressBar(
|
|
|
1384
1499
|
cells: number,
|
|
1385
1500
|
): string {
|
|
1386
1501
|
const safeCells = Math.max(1, cells);
|
|
1387
|
-
const visibleProgress =
|
|
1388
|
-
|
|
1389
|
-
|
|
1502
|
+
const visibleProgress =
|
|
1503
|
+
summary.running > 0
|
|
1504
|
+
? summary.completed + summary.running
|
|
1505
|
+
: summary.completed;
|
|
1390
1506
|
const filled =
|
|
1391
1507
|
summary.total <= 0
|
|
1392
1508
|
? 0
|
|
@@ -1420,9 +1536,31 @@ function statusText(status: WorkflowRunStatus | TaskRunStatus): string {
|
|
|
1420
1536
|
return status;
|
|
1421
1537
|
}
|
|
1422
1538
|
|
|
1539
|
+
function healthColor(health: WorkflowProgressHealth): string {
|
|
1540
|
+
return health.tone;
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
function healthGlyph(theme: Theme, health: WorkflowProgressHealth): string {
|
|
1544
|
+
if (health.tone === "success") return success(theme, "✓");
|
|
1545
|
+
if (health.tone === "warning") return warning(theme, "●");
|
|
1546
|
+
if (health.tone === "error") return errorText(theme, "●");
|
|
1547
|
+
if (health.tone === "dim") return muted(theme, "•");
|
|
1548
|
+
return accent(theme, "●");
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
function healthLabel(theme: Theme, health: WorkflowProgressHealth): string {
|
|
1552
|
+
return fg(theme, healthColor(health), strong(theme, health.label));
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
function healthInline(theme: Theme, health: WorkflowProgressHealth): string {
|
|
1556
|
+
if (health.state === "completed" || health.state === "pending") return "";
|
|
1557
|
+
return `${healthGlyph(theme, health)} ${healthLabel(theme, health)}`;
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1423
1560
|
function taskListStatusLabel(
|
|
1424
1561
|
theme: Theme,
|
|
1425
1562
|
task: WorkflowTaskRunRecord,
|
|
1563
|
+
health: WorkflowProgressHealth,
|
|
1426
1564
|
): string {
|
|
1427
1565
|
const validation = taskValidationSummary(task);
|
|
1428
1566
|
const label =
|
|
@@ -1432,8 +1570,14 @@ function taskListStatusLabel(
|
|
|
1432
1570
|
? "valid"
|
|
1433
1571
|
: task.status === "completed"
|
|
1434
1572
|
? "done"
|
|
1435
|
-
:
|
|
1436
|
-
|
|
1573
|
+
: task.status === "running"
|
|
1574
|
+
? health.label
|
|
1575
|
+
: statusText(task.status);
|
|
1576
|
+
const suffix =
|
|
1577
|
+
task.status === "running" && health.currentTask?.elapsedMs !== undefined
|
|
1578
|
+
? ` ${muted(theme, "·")} ${metaValue(theme, formatDuration(health.currentTask.elapsedMs))}`
|
|
1579
|
+
: "";
|
|
1580
|
+
return `${fg(theme, task.status === "running" ? healthColor(health) : statusColor(task.status), strong(theme, label))}${suffix}`;
|
|
1437
1581
|
}
|
|
1438
1582
|
|
|
1439
1583
|
function compactStatusLabel(
|
|
@@ -1452,7 +1596,6 @@ function shortId(runId: string): string {
|
|
|
1452
1596
|
return runId.replace(/^workflow_/, "workflow_").slice(0, 24);
|
|
1453
1597
|
}
|
|
1454
1598
|
|
|
1455
|
-
|
|
1456
1599
|
function visibleWindow<T>(
|
|
1457
1600
|
items: T[],
|
|
1458
1601
|
selectedIndex: number,
|
|
@@ -1518,7 +1661,7 @@ function taskValidationSummary(
|
|
|
1518
1661
|
const issueMessage =
|
|
1519
1662
|
typeof issue === "string"
|
|
1520
1663
|
? issue
|
|
1521
|
-
: issue?.message ?? issue?.path ?? issue?.code ?? "";
|
|
1664
|
+
: (issue?.message ?? issue?.path ?? issue?.code ?? "");
|
|
1522
1665
|
const message = validation.message ?? validation.reason ?? issueMessage;
|
|
1523
1666
|
if (status === "valid" && !message) return undefined;
|
|
1524
1667
|
return { status, message };
|
|
@@ -1726,7 +1869,10 @@ function truncateToWidth(text: string, width: number): string {
|
|
|
1726
1869
|
if (safeWidth === 0) return "";
|
|
1727
1870
|
if (visibleWidth(text) <= safeWidth) return text;
|
|
1728
1871
|
|
|
1729
|
-
const hasAnsi =
|
|
1872
|
+
const hasAnsi =
|
|
1873
|
+
text.includes("\u001b[") ||
|
|
1874
|
+
text.includes("\u001b]") ||
|
|
1875
|
+
text.includes("\u001b_");
|
|
1730
1876
|
const ellipsis = "…";
|
|
1731
1877
|
const ellipsisWidth = visibleWidth(ellipsis);
|
|
1732
1878
|
const limit = Math.max(0, safeWidth - ellipsisWidth);
|
|
@@ -1905,7 +2051,6 @@ function pathText(theme: Theme, projectPath: string): string {
|
|
|
1905
2051
|
return `${dim(theme, projectPath.slice(0, lastSlash + 1))}${metaValue(theme, projectPath.slice(lastSlash + 1))}`;
|
|
1906
2052
|
}
|
|
1907
2053
|
|
|
1908
|
-
|
|
1909
2054
|
function navHint(theme: Theme, text: string): string {
|
|
1910
2055
|
return text
|
|
1911
2056
|
.split(" · ")
|