Files
maestro/ui/src/lib/delegateRuns.ts
T
oss-sync b1292e34b2
CI / build-and-test (push) Has been cancelled
sync: update from private repo (edc775f2)
2026-07-06 01:04:12 +00:00

74 lines
2.6 KiB
TypeScript

export interface DelegateRun {
delegateRunId: string;
parentRunId: string | null;
description: string;
depth: number;
status: 'success' | 'aborted' | 'needs_user_input' | 'running';
startTs: string;
endTs: string | null;
eventCount: number;
toolCalls: number;
}
export interface DelegateRunNode extends DelegateRun {
children: DelegateRunNode[];
}
/** サブタスクジョブに紐づく delegate run のグループ。Task 7 でレンダリングされる。 */
export interface SubtaskDelegateGroup {
jobId: string;
issueNumber: number;
depth: number;
status: string;
runs: DelegateRun[];
}
/** GET /:id/delegate-runs のレスポンス型。 */
export interface DelegateRunsResult {
runs: DelegateRun[];
subtasks: SubtaskDelegateGroup[];
}
/** delegate run の status に対応する i18n ラベルキーと Tailwind 色クラス。 */
export function delegateStatusBadge(
status: DelegateRun['status'],
): { labelKey: string; cls: string } {
switch (status) {
case 'success': return { labelKey: 'subtasks.delegateStatus.success', cls: 'bg-emerald-100 text-emerald-800' };
case 'aborted': return { labelKey: 'subtasks.delegateStatus.aborted', cls: 'bg-red-100 text-red-800' };
case 'needs_user_input': return { labelKey: 'subtasks.delegateStatus.needs_user_input', cls: 'bg-amber-100 text-amber-800' };
default: return { labelKey: 'subtasks.delegateStatus.running', cls: 'bg-blue-100 text-blue-800' };
}
}
/** endTs があればそこまで、無ければ now までの経過。clock skew で負にならないようガード。 */
export function formatElapsed(startTs: string, endTs: string | null, now: number): string {
const end = endTs ? new Date(endTs).getTime() : now;
const ms = Math.max(0, end - new Date(startTs).getTime());
if (ms < 1000) return `${ms}ms`;
if (ms < 60_000) return `${(ms / 1000).toFixed(1)}s`;
return `${Math.floor(ms / 60_000)}m${Math.floor((ms % 60_000) / 1000)}s`;
}
/**
* Build a tree from a flat list of DelegateRun records.
* Runs whose parentRunId is null, or whose parent is not in the set,
* are treated as roots (orphan fallback — no data loss).
*/
export function buildDelegateRunTree(runs: DelegateRun[]): DelegateRunNode[] {
const nodes = new Map<string, DelegateRunNode>();
for (const r of runs) {
nodes.set(r.delegateRunId, { ...r, children: [] });
}
const roots: DelegateRunNode[] = [];
for (const node of nodes.values()) {
const parent = node.parentRunId ? nodes.get(node.parentRunId) : undefined;
if (parent) {
parent.children.push(node);
} else {
roots.push(node);
}
}
return roots;
}