吸收数字员工管理端事件视图

This commit is contained in:
baiyanyun
2026-06-10 15:16:23 +08:00
parent 4f489f402a
commit 13e48df63d
3 changed files with 344 additions and 13 deletions

View File

@@ -105,6 +105,16 @@ interface SyncResponse {
message?: string;
}
interface EventBusinessDetail {
plan_id: string | null;
task_id: string | null;
run_id: string | null;
kind: string | null;
message: string | null;
payload: Record<string, unknown>;
occurred_at: string | null;
}
export interface DigitalEmployeeSyncStatus {
running: boolean;
syncing: boolean;
@@ -469,15 +479,7 @@ function buildBusinessView(
finished_at: stringField(record.finishedAt ?? record.finished_at) || null,
};
case "event":
return {
...base,
plan_id: stringField(record.plan_id ?? record.planId) || null,
task_id: stringField(record.task_id ?? record.taskId) || null,
run_id: stringField(record.run_id ?? record.runId) || null,
kind: stringField(record.kind) || summary?.status || null,
message: stringField(record.message) || summary?.summary || null,
occurred_at: stringField(record.occurred_at ?? record.occurredAt) || null,
};
return buildEventBusinessView(base, row, record, summary);
case "artifact":
return {
...base,
@@ -509,6 +511,149 @@ function buildBusinessView(
}
}
function buildEventBusinessView(
base: Record<string, unknown>,
row: OutboxRow,
record: Record<string, unknown>,
summary: DigitalEmployeeSyncEntitySummary | null,
): Record<string, unknown> {
const detail = readEventBusinessDetail(row.entity_id, record, summary);
const kind = detail.kind || "event";
return {
...base,
category: eventBusinessCategory(kind),
plan_id: detail.plan_id,
task_id: detail.task_id,
run_id: detail.run_id,
kind,
message: detail.message,
occurred_at: detail.occurred_at,
...eventSpecificBusinessView(kind, detail.payload, row.entity_id),
};
}
function readEventBusinessDetail(
eventId: string,
fallback: Record<string, unknown>,
summary: DigitalEmployeeSyncEntitySummary | null,
): EventBusinessDetail {
const db = getDigitalEmployeeDb();
const row = db?.prepare(`
SELECT plan_id, task_id, run_id, kind, message, payload, occurred_at
FROM digital_events
WHERE id = ?
`).get(eventId) as Record<string, unknown> | undefined;
const eventPayload = parsePayload(stringField(row?.payload) || "{}");
const fallbackPayload = objectRecord(fallback.payload) ?? fallback;
return {
plan_id: stringField(row?.plan_id ?? fallback.plan_id ?? fallback.planId) || null,
task_id: stringField(row?.task_id ?? fallback.task_id ?? fallback.taskId) || null,
run_id: stringField(row?.run_id ?? fallback.run_id ?? fallback.runId) || null,
kind: stringField(row?.kind ?? fallback.kind) || summary?.status || null,
message: stringField(row?.message ?? fallback.message) || summary?.summary || null,
payload: objectRecord(eventPayload) ?? fallbackPayload,
occurred_at: stringField(row?.occurred_at ?? fallback.occurred_at ?? fallback.occurredAt) || null,
};
}
function eventSpecificBusinessView(
kind: string,
payload: Record<string, unknown>,
eventId: string,
): Record<string, unknown> {
if (kind === "route_decision") return routeDecisionBusinessView(payload, eventId);
if (kind.startsWith("approval_action_")) return approvalActionBusinessView(payload);
if (kind.startsWith("artifact_access_") || kind.startsWith("artifact_retention_") || kind === "artifact_version_observed") {
return artifactEventBusinessView(payload);
}
if (kind.startsWith("skill_call_")) return skillCallBusinessView(payload);
if (kind.startsWith("plan_step_dispatch_")) return planStepDispatchBusinessView(payload);
return {};
}
function routeDecisionBusinessView(payload: Record<string, unknown>, eventId: string): Record<string, unknown> {
const decision = objectRecord(payload.routeDecision ?? payload.route_decision) ?? payload;
const policyResult = stringField(decision.policy_result ?? decision.policyResult) || "accepted";
const reasonCodes = stringArrayField(decision.reason_codes ?? decision.reasonCodes);
const createdCommandId = stringField(decision.created_command_id ?? decision.createdCommandId) || null;
return {
route_decision_id: stringField(decision.id) || eventId,
route_kind: stringField(decision.route_kind ?? decision.routeKind) || "ChatOnly",
intent_kind: stringField(decision.intent_kind ?? decision.intentKind) || "chat",
policy_result: policyResult,
reason_codes: reasonCodes,
candidate_skills: stringArrayField(decision.candidate_skills ?? decision.candidateSkills),
created_command_id: createdCommandId,
attention_level: routeDecisionAttentionLevel(policyResult, reasonCodes, createdCommandId),
};
}
function approvalActionBusinessView(payload: Record<string, unknown>): Record<string, unknown> {
const comment = objectRecord(payload.comment_summary);
const delegate = objectRecord(payload.delegate_summary);
const sla = objectRecord(payload.sla_summary);
const risk = objectRecord(payload.risk_summary);
return {
approval_id: stringField(payload.approval_id ?? payload.approvalId) || null,
action: stringField(payload.action) || null,
status: stringField(payload.status) || null,
actor: stringField(payload.actor) || null,
risk_level: stringField(risk?.risk_level ?? payload.risk_level) || null,
due_at: stringField(sla?.due_at ?? payload.due_at) || null,
delegate_to: stringField(delegate?.target ?? delegate?.label ?? payload.delegate_to) || null,
comment_length: numberField(comment?.length ?? payload.comment_length),
};
}
function artifactEventBusinessView(payload: Record<string, unknown>): Record<string, unknown> {
const retention = objectRecord(payload.retention_summary);
return {
artifact_id: stringField(payload.artifact_id ?? payload.artifactId) || null,
action: stringField(payload.action) || null,
status: stringField(payload.status) || null,
retention_status: stringField(payload.retention_status ?? retention?.retention_status) || null,
retention_until: stringField(payload.retention_until ?? retention?.retention_until) || null,
actor: stringField(payload.actor) || null,
};
}
function skillCallBusinessView(payload: Record<string, unknown>): Record<string, unknown> {
return {
call_id: stringField(payload.call_id ?? payload.callId) || null,
source: stringField(payload.source) || null,
server_id: stringField(payload.server_id ?? payload.serverId) || null,
tool_name: stringField(payload.tool_name ?? payload.toolName) || null,
skill_id: stringField(payload.skill_id ?? payload.skillId) || null,
decision: stringField(payload.decision) || null,
reason_codes: stringArrayField(payload.reason_codes ?? payload.reasonCodes),
};
}
function planStepDispatchBusinessView(payload: Record<string, unknown>): Record<string, unknown> {
return {
step_id: stringField(payload.step_id ?? payload.stepId) || null,
status: stringField(payload.status) || null,
reason: stringField(payload.reason) || null,
};
}
function eventBusinessCategory(kind: string): string {
if (kind === "route_decision") return "route";
if (kind.startsWith("approval_")) return "approval";
if (kind.startsWith("artifact_")) return "artifact";
if (kind.startsWith("skill_call_")) return "skill";
if (kind.startsWith("plan_step_dispatch_")) return "dispatch";
if (kind.startsWith("governance_")) return "governance";
return "runtime";
}
function routeDecisionAttentionLevel(policyResult: string, reasonCodes: string[], createdCommandId: string | null): "info" | "action" | "warn" | "error" {
if (policyResult && policyResult !== "accepted") return "error";
if (reasonCodes.includes("candidate_skills_disabled") || reasonCodes.includes("route_action_missing_context")) return "warn";
if (createdCommandId) return "action";
return "info";
}
function compactPreview(value: string, maxLength = 120): string | null {
const compact = value.replace(/\s+/g, " ").trim();
if (!compact) return null;
@@ -799,6 +944,20 @@ function stringField(value: unknown): string {
return typeof value === "string" && value.trim() ? value.trim() : "";
}
function stringArrayField(value: unknown): string[] {
if (!Array.isArray(value)) return [];
return value.map(stringField).filter(Boolean);
}
function numberField(value: unknown): number | null {
if (typeof value === "number" && Number.isFinite(value)) return value;
if (typeof value === "string" && value.trim()) {
const parsed = Number(value);
return Number.isFinite(parsed) ? parsed : null;
}
return null;
}
function objectRecord(value: unknown): Record<string, unknown> | null {
return value && typeof value === "object" && !Array.isArray(value)
? value as Record<string, unknown>