File size: 4,924 Bytes
5f40163
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
import { I18nKey } from "#/i18n/declaration";
import {
  AutomationRunStatus,
  type AutomationRun,
  type AutomationTaskOutcomeStatus,
} from "#/types/automation";

export type AutomationRunBadgeStatus =
  | AutomationRunStatus
  | AutomationTaskOutcomeStatus;

export interface AutomationRunTaskOutcome {
  status: AutomationTaskOutcomeStatus;
  outcomeSummary: string | null;
}

export interface AutomationRunDisplay {
  badgeStatus: AutomationRunBadgeStatus;
  summary: string | null;
  taskOutcome: AutomationRunTaskOutcome | null;
  customTaskMetadata: unknown | null;
  customTaskMetadataText: string | null;
}

const TASK_OUTCOME_STATUSES: readonly AutomationTaskOutcomeStatus[] = [
  "success",
  "partial_success",
  "blocked",
  "failed",
  "unknown",
];

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}

export function isTaskOutcomeStatus(
  value: unknown,
): value is AutomationTaskOutcomeStatus {
  return (
    typeof value === "string" &&
    TASK_OUTCOME_STATUSES.includes(value as AutomationTaskOutcomeStatus)
  );
}

function trimString(value: unknown): string | null {
  if (typeof value !== "string") return null;
  return value.trim() || null;
}

const DEFAULT_FINISH_TOOL_RESPONSE_KEYS = new Set([
  "status",
  "outcome_summary",
]);

export function getAutomationRunFinishToolResponse(
  run: AutomationRun,
): unknown | null {
  const response = run.run_metadata?.finish_tool_response;
  return response === undefined || response === null ? null : response;
}

export function formatAutomationRunTaskMetadata(
  metadata: unknown,
): string | null {
  const trimmed = trimString(metadata);
  if (trimmed) return trimmed;
  if (typeof metadata === "number" || typeof metadata === "boolean") {
    return String(metadata);
  }
  if (metadata === null || metadata === undefined) return null;

  try {
    return JSON.stringify(metadata, null, 2);
  } catch {
    return String(metadata);
  }
}

function getCustomFinishToolMetadata(response: unknown): unknown | null {
  if (response === null || response === undefined) return null;
  if (!isRecord(response)) return response;

  const customEntries = Object.entries(response).filter(
    ([key]) => !DEFAULT_FINISH_TOOL_RESPONSE_KEYS.has(key),
  );
  if (customEntries.length > 0) return Object.fromEntries(customEntries);

  if ("status" in response && !isTaskOutcomeStatus(response.status)) {
    return { status: response.status };
  }

  return null;
}

export function getAutomationRunTaskOutcome(
  run: AutomationRun,
): AutomationRunTaskOutcome | null {
  const response = getAutomationRunFinishToolResponse(run);
  if (!isRecord(response)) return null;

  const outcomeSummary = trimString(response.outcome_summary);
  const hasStatus = typeof response.status === "string";
  if (!hasStatus && !outcomeSummary) return null;

  return {
    status: isTaskOutcomeStatus(response.status) ? response.status : "unknown",
    outcomeSummary,
  };
}

export function getAutomationRunDisplay(
  run: AutomationRun,
): AutomationRunDisplay {
  const finishToolResponse = getAutomationRunFinishToolResponse(run);
  const customTaskMetadata = getCustomFinishToolMetadata(finishToolResponse);
  const customTaskMetadataText =
    formatAutomationRunTaskMetadata(customTaskMetadata);
  const taskOutcome = getAutomationRunTaskOutcome(run);
  const systemSummary = trimString(run.error_detail);

  if (run.status === AutomationRunStatus.COMPLETED) {
    return {
      badgeStatus:
        taskOutcome?.status ??
        (finishToolResponse !== null ? "unknown" : "success"),
      summary: taskOutcome?.outcomeSummary ?? null,
      taskOutcome,
      customTaskMetadata,
      customTaskMetadataText,
    };
  }

  return {
    badgeStatus: run.status,
    summary: systemSummary,
    taskOutcome,
    customTaskMetadata,
    customTaskMetadataText,
  };
}

export function getAutomationRunBadgeLabelKey(
  status: AutomationRunBadgeStatus | string,
): I18nKey {
  switch (status) {
    case AutomationRunStatus.COMPLETED:
    case "success":
      return I18nKey.AUTOMATIONS$DETAIL$SUCCESSFUL;
    case AutomationRunStatus.FAILED:
    case "failed":
      return I18nKey.AUTOMATIONS$DETAIL$FAILED;
    case AutomationRunStatus.PENDING:
      return I18nKey.AUTOMATIONS$DETAIL$PENDING;
    case AutomationRunStatus.RUNNING:
      return I18nKey.AUTOMATIONS$DETAIL$RUNNING;
    case AutomationRunStatus.CANCELLED:
      return I18nKey.AUTOMATIONS$DETAIL$CANCELLED;
    case AutomationRunStatus.SKIPPED:
      return I18nKey.AUTOMATIONS$DETAIL$SKIPPED;
    case "blocked":
      return I18nKey.AUTOMATIONS$DETAIL$BLOCKED;
    case "partial_success":
      return I18nKey.AUTOMATIONS$DETAIL$PARTIAL;
    case "unknown":
      return I18nKey.AUTOMATIONS$DETAIL$NEEDS_REVIEW;
    default:
      return I18nKey.FEATURED_AUTOMATIONS$STATUS_UNKNOWN;
  }
}