吸收数字员工技能调用审计

This commit is contained in:
baiyanyun
2026-06-10 09:24:02 +08:00
parent 91540dec0c
commit cc36d8dcaf
15 changed files with 894 additions and 73 deletions

View File

@@ -655,6 +655,10 @@ export async function handleQimingclawDigitalApi<T>(
const rows = filterDailyReportRows(dailyReportRows(await readQimingclawSnapshot()), url.searchParams);
return { ...paginateBusinessRows(rows, url.searchParams), source: BUSINESS_VIEW_SOURCE } as T;
}
if (method === 'GET' && pathname === '/api/digital-employee/skill-call-audits') {
const rows = filterSkillCallAuditRows(skillCallAuditRows(await readQimingclawSnapshot()), url.searchParams);
return { ...paginateBusinessRows(rows, url.searchParams), source: BUSINESS_VIEW_SOURCE } as T;
}
const businessPlanDetailMatch = pathname.match(/^\/api\/digital-employee\/plans\/([^/]+)$/);
if (method === 'GET' && businessPlanDetailMatch) {
const planId = decodeURIComponent(businessPlanDetailMatch[1] || '');
@@ -2324,6 +2328,46 @@ function businessApprovalRows(snapshot: QimingclawSnapshot | null): BusinessView
});
}
function skillCallAuditRows(snapshot: QimingclawSnapshot | null): BusinessViewRow[] {
return runtimeEvents(snapshot)
.filter((event) => ['skill_call_allowed', 'skill_call_rejected', 'skill_call_observed'].includes(event.kind))
.map((event) => {
const payload = asRecord(event.payload) ?? {};
return {
audit_id: event.id,
event_id: event.id,
remote_id: event.remoteId ?? null,
decision: stringValue(payload.decision) || event.kind.replace('skill_call_', ''),
source: stringValue(payload.source) || null,
server_id: stringValue(payload.server_id) || null,
tool_name: stringValue(payload.tool_name) || null,
skill_id: stringValue(payload.skill_id) || null,
call_id: stringValue(payload.call_id) || null,
session_id: stringValue(payload.session_id) || null,
task_id: (event.taskId ?? stringValue(payload.task_id)) || null,
run_id: (event.runId ?? stringValue(payload.run_id)) || null,
reason_codes: Array.isArray(payload.reason_codes) ? payload.reason_codes : [],
message: event.message,
occurred_at: event.occurredAt,
sync_status: event.syncStatus ?? null,
payload,
};
})
.sort((left, right) => stringValue(right.occurred_at).localeCompare(stringValue(left.occurred_at)));
}
function filterSkillCallAuditRows(rows: BusinessViewRow[], searchParams: URLSearchParams): BusinessViewRow[] {
const skillId = stringValue(searchParams.get('skill_id'));
const serverId = stringValue(searchParams.get('server_id'));
const toolName = stringValue(searchParams.get('tool_name'));
const decision = stringValue(searchParams.get('decision'));
return rows
.filter((row) => !skillId || stringValue(row.skill_id) === skillId)
.filter((row) => !serverId || stringValue(row.server_id) === serverId)
.filter((row) => !toolName || stringValue(row.tool_name) === toolName)
.filter((row) => !decision || stringValue(row.decision) === decision);
}
function dailyReportRows(snapshot: QimingclawSnapshot | null): BusinessViewRow[] {
return buildArtifacts(snapshot)
.filter(isDailyReportArtifact)