import { formatAgentRunElapsed, formatAgentRunProgress, resolveAgentRunHeartbeat, resolveAgentRunStatus } from '../../utils/agentRunMonitor.js' const SOURCE_LABELS = { schedule: '定时任务', system_event: '系统事件', user_message: '用户触发' } const KNOWLEDGE_JOB_TYPES = new Set([ 'knowledge_index_sync', 'llm_wiki_sync', 'llm_wiki_rule_formation', 'finance_policy_knowledge_organize' ]) export const VISIBLE_DIGITAL_EMPLOYEE_WORK_TASK_TYPES = new Set([ 'finance_dashboard_snapshot', 'digital_employee_reminder_scan', 'employee_behavior_profile_scan', 'department_expense_baseline_accumulate', 'budget_overrun_precontrol_evaluate', 'multi_evidence_consistency_evaluate', 'travel_spatiotemporal_consistency_evaluate', 'global_risk_scan', 'finance_policy_knowledge_organize' ]) const DAILY_COMPACT_TASK_TYPES = new Set([ 'finance_dashboard_snapshot' ]) const TASK_TYPE_LABELS = { finance_dashboard_snapshot: '财务经营快照沉淀', digital_employee_reminder_scan: '定时提醒与待办扫描', global_risk_scan: '财务风险图谱巡检', employee_behavior_profile_scan: '员工行为画像巡检', department_expense_baseline_accumulate: '部门费用基线沉淀', budget_overrun_precontrol_evaluate: '预算占用与超标预警', multi_evidence_consistency_evaluate: '单据多凭证一致性评估', travel_spatiotemporal_consistency_evaluate: '差旅时空一致性评估', risk_clue_collect: '风险线索归集', finance_policy_knowledge_organize: '知识制度整理', knowledge_index_sync: '知识制度整理', llm_wiki_sync: '知识制度整理', llm_wiki_rule_formation: '知识制度整理' } const TASK_CODE_TO_TYPE = { 'task.hermes.finance_dashboard_snapshot': 'finance_dashboard_snapshot', 'task.hermes.digital_employee_reminder_scan': 'digital_employee_reminder_scan', 'task.hermes.global_risk_scan': 'global_risk_scan', 'task.hermes.employee_behavior_profile_scan': 'employee_behavior_profile_scan', 'task.hermes.department_expense_baseline_accumulate': 'department_expense_baseline_accumulate', 'task.hermes.budget_overrun_precontrol_evaluate': 'budget_overrun_precontrol_evaluate', 'task.hermes.multi_evidence_consistency_evaluate': 'multi_evidence_consistency_evaluate', 'task.hermes.travel_spatiotemporal_consistency_evaluate': 'travel_spatiotemporal_consistency_evaluate', 'task.hermes.finance_policy_knowledge_organize': 'finance_policy_knowledge_organize', 'task.hermes.risk_rule_discovery': 'risk_clue_collect' } function toObject(value) { return value && typeof value === 'object' && !Array.isArray(value) ? value : {} } function normalizeTaskType(value) { const normalized = String(value || '').trim() if (!normalized) { return '' } return TASK_CODE_TO_TYPE[normalized] || normalized } function resolveTaskTypeFromToolName(value) { const name = String(value || '').trim() if (name.includes('financial_risk_graph')) { return 'global_risk_scan' } if (name.includes('finance_dashboard_snapshot') || name.includes('finance_dashboard')) { return 'finance_dashboard_snapshot' } if (name.includes('digital_employee_reminder') || name.includes('reminder')) { return 'digital_employee_reminder_scan' } if (name.includes('employee_behavior_profile')) { return 'employee_behavior_profile_scan' } if (name.includes('finance_policy_knowledge')) { return 'finance_policy_knowledge_organize' } if (name.includes('risk_clue')) { return 'risk_clue_collect' } return '' } export function formatWorkRecordDateTime(value) { if (!value) { return '未结束' } const date = new Date(value) if (Number.isNaN(date.getTime())) { return String(value) } return date.toLocaleString('zh-CN', { hour12: false }) } export function formatWorkRecordSummary(summary) { const text = String(summary || '').trim() if (!text) { return '暂无摘要。' } if (text.length <= 64) { return text } return `${text.slice(0, 64)}...` } export function resolveWorkRecordSourceLabel(source) { return SOURCE_LABELS[source] || source || '未标记' } export function resolveWorkRecordTaskType(run) { const routeJson = toObject(run?.route_json) const routeCandidates = [ routeJson.job_type, routeJson.task_type, routeJson.report_type, routeJson.task_code ].map(normalizeTaskType) for (const candidate of routeCandidates) { if (candidate) { return candidate } } for (const toolCall of run?.tool_calls || []) { const requestJson = toObject(toolCall?.request_json) const responseJson = toObject(toolCall?.response_json) const candidates = [ requestJson.task_type, requestJson.job_type, responseJson.report_type, responseJson.task_type, responseJson.job_type, resolveTaskTypeFromToolName(toolCall?.tool_name) ].map(normalizeTaskType) const matched = candidates.find(Boolean) if (matched) { return matched } } return '' } export function isVisibleDigitalEmployeeWorkRecord(run) { const taskType = resolveWorkRecordTaskType(run) return VISIBLE_DIGITAL_EMPLOYEE_WORK_TASK_TYPES.has(taskType) } function resolveWorkRecordDayKey(run) { const date = new Date(run?.started_at || run?.finished_at || '') if (Number.isNaN(date.getTime())) { return 'unknown' } return date.toISOString().slice(0, 10) } export function compactDigitalEmployeeWorkRecords(items = []) { const rows = [] const compactedKeys = new Set() for (const run of items) { const taskType = resolveWorkRecordTaskType(run) if (!VISIBLE_DIGITAL_EMPLOYEE_WORK_TASK_TYPES.has(taskType)) { continue } if (DAILY_COMPACT_TASK_TYPES.has(taskType)) { const key = `${taskType}:${resolveWorkRecordDayKey(run)}` if (compactedKeys.has(key)) { continue } compactedKeys.add(key) } rows.push(run) } return rows } export function resolveWorkRecordTaskLabel(run) { const taskType = resolveWorkRecordTaskType(run) return TASK_TYPE_LABELS[taskType] || '' } export function resolveWorkRecordProductKind(run) { const taskType = resolveWorkRecordTaskType(run) if (taskType === 'finance_dashboard_snapshot') { return 'finance_snapshot' } if (taskType === 'digital_employee_reminder_scan') { return 'reminder_scan' } if (taskType === 'global_risk_scan') { return 'risk_graph' } if (taskType === 'employee_behavior_profile_scan') { return 'employee_profile' } if (taskType === 'risk_clue_collect') { return 'risk_clue' } if (KNOWLEDGE_JOB_TYPES.has(taskType)) { return 'knowledge' } return '' } export function extractWorkRecordToolSummary(run) { const taskType = resolveWorkRecordTaskType(run) const toolCalls = Array.isArray(run?.tool_calls) ? run.tool_calls : [] const matchedCall = toolCalls.find((toolCall) => { const requestJson = toObject(toolCall?.request_json) const responseJson = toObject(toolCall?.response_json) const candidates = [ requestJson.task_type, requestJson.job_type, responseJson.report_type, responseJson.task_type, responseJson.job_type, resolveTaskTypeFromToolName(toolCall?.tool_name) ].map(normalizeTaskType) return candidates.includes(taskType) }) || toolCalls[0] const responseJson = toObject(matchedCall?.response_json) const nestedSummary = toObject(responseJson.summary) return Object.keys(nestedSummary).length ? nestedSummary : responseJson } export function resolveWorkRecordModuleLabel(run) { const routeJson = run?.route_json || {} const taskLabel = resolveWorkRecordTaskLabel(run) if (taskLabel) { return taskLabel } if (KNOWLEDGE_JOB_TYPES.has(routeJson.job_type)) { return '知识制度整理' } if (routeJson.selected_agent) { return '数字员工' } if (routeJson.folder) { return String(routeJson.folder) } return resolveWorkRecordSourceLabel(run?.source) } export function resolveWorkRecordTitle(run) { const routeJson = run?.route_json || {} const taskLabel = resolveWorkRecordTaskLabel(run) if (taskLabel) { const suffix = String(routeJson.task_name || routeJson.folder || '本次运行').trim() return suffix && suffix !== taskLabel ? `${taskLabel} · ${suffix}` : taskLabel } if (KNOWLEDGE_JOB_TYPES.has(routeJson.job_type)) { return `知识制度整理 · ${routeJson.folder || '未指定目录'}` } return `数字员工调用 · ${resolveWorkRecordModuleLabel(run)}` } export function resolveWorkRecordStatusLabel(run) { return resolveAgentRunStatus(run).label } export function resolveWorkRecordStatusTone(run) { return resolveAgentRunStatus(run).tone } export function resolveWorkRecordStatusNote(run) { const statusInfo = resolveAgentRunStatus(run) if (statusInfo.note) { return statusInfo.note } const heartbeat = resolveAgentRunHeartbeat(run) if (heartbeat.at !== null) { return `最后心跳 ${formatWorkRecordDateTime(heartbeat.at)}` } return '暂无额外状态' } export function resolveWorkRecordSummaryMeta(run) { const statusInfo = resolveAgentRunStatus(run) const progressText = formatAgentRunProgress(run) const elapsedLabel = run?.status === 'running' ? '已运行' : '耗时' const elapsedText = formatAgentRunElapsed(run) const parts = [`阶段 ${statusInfo.phaseLabel}`] if (progressText) { parts.push(progressText) } if (elapsedText !== '—') { parts.push(`${elapsedLabel} ${elapsedText}`) } return parts.join(' · ') }