feat(client): persist digital governance events

This commit is contained in:
baiyanyun
2026-06-07 18:59:59 +08:00
parent 5e76828afe
commit e37024bdbe
14 changed files with 925 additions and 158 deletions

View File

@@ -203,6 +203,19 @@ export interface DigitalEmployeeRuntimeEventRecord {
payload?: unknown;
}
export interface DigitalEmployeeGovernanceCommandRecord {
commandKind: string;
commandId: string;
correlationId?: string;
planId?: string | null;
scheduleId?: string | null;
accepted?: boolean;
message?: string;
error?: string | null;
payload?: Record<string, unknown> | null;
result?: Record<string, unknown> | null;
}
function defaultState(): DigitalEmployeeState {
return {
version: 1,
@@ -681,6 +694,140 @@ export function recordDigitalEmployeeRuntimeEvent(
tx();
}
export function recordDigitalEmployeeGovernanceCommand(
command: DigitalEmployeeGovernanceCommandRecord,
): { planId: string; taskId: string; runId: string } {
const now = new Date().toISOString();
const commandId = command.commandId || `governance-${Date.now()}`;
const result = objectRecord(command.result) ?? {};
const payload = objectRecord(command.payload) ?? {};
const planId = stringValue(command.planId)
|| stringValue(result.plan_id)
|| stringValue(payload.plan_id)
|| `governance-plan-${safeId(commandId)}`;
const ids = {
planId,
taskId: `governance-task-${safeId(commandId)}`,
runId: `governance-run-${safeId(commandId)}`,
};
const title = stringValue(payload.title ?? result.title)
|| governanceCommandTitle(command.commandKind);
const status = governanceCommandStatus(command);
const payloadEnvelope = {
source: "qimingclaw-governance-command",
commandKind: command.commandKind,
commandId,
correlationId: command.correlationId,
planId,
scheduleId: command.scheduleId || stringValue(result.schedule_id) || null,
accepted: command.accepted !== false,
message: command.message,
error: command.error,
payload,
result,
};
const db = getDigitalEmployeeDb();
if (!db) return ids;
const tx = db.transaction(() => {
upsertPlan({
id: ids.planId,
title,
objective: stringValue(payload.objective ?? payload.description ?? payload.requested_action)
|| command.message
|| title,
status,
payload: payloadEnvelope,
now,
});
upsertTask({
id: ids.taskId,
planId: ids.planId,
title: governanceCommandTitle(command.commandKind),
status,
assignedSkillId: "qimingclaw-governance-command",
payload: payloadEnvelope,
now,
});
upsertRun({
id: ids.runId,
planId: ids.planId,
taskId: ids.taskId,
status,
payload: payloadEnvelope,
now,
});
if (status !== "running" && status !== "pending") {
finishRun(ids.runId, status, now);
}
persistPayloadArtifactsAndApprovals(ids, payloadEnvelope, now);
insertEvent(
{
event_id: `${ids.runId}:${command.commandKind}:${safeId(commandId)}`,
kind: `governance_${command.commandKind}`,
occurred_at: now,
message: command.message || `${governanceCommandTitle(command.commandKind)} 已记录`,
plan_id: ids.planId,
task_id: ids.taskId,
},
ids.runId,
payloadEnvelope,
);
});
tx();
return ids;
}
function governanceCommandTitle(commandKind: string): string {
switch (commandKind) {
case "create_plan":
return "创建数字员工计划";
case "submit_plan":
return "提交数字员工计划";
case "approve_plan":
return "批准数字员工计划";
case "activate_plan":
return "激活数字员工计划";
case "create_schedule":
return "创建数字员工定时任务";
case "run_schedule_now":
return "立即运行数字员工计划";
case "pause_plan":
return "暂停数字员工计划";
case "cancel_plan":
return "取消数字员工计划";
default:
return `数字员工治理命令:${commandKind || "unknown"}`;
}
}
function governanceCommandStatus(
command: DigitalEmployeeGovernanceCommandRecord,
): string {
if (command.accepted === false || command.error) return "failed";
const resultStatus = stringValue(command.result?.status);
if (resultStatus) return resultStatus;
switch (command.commandKind) {
case "activate_plan":
case "run_schedule_now":
return "running";
case "create_plan":
return "draft";
case "submit_plan":
return "reviewing";
case "approve_plan":
return "approved";
case "pause_plan":
return "paused";
case "cancel_plan":
return "cancelled";
default:
return "completed";
}
}
function persistSnapshotTables(
snapshot: DigitalEmployeeSnapshot,
event: DigitalEmployeeStateEvent | null,