74 lines
2.6 KiB
TypeScript
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;
|
|
}
|