吸收数字员工步骤治理闭环

This commit is contained in:
baiyanyun
2026-06-09 11:31:29 +08:00
parent 5ebc0a640b
commit ef8252e2dd
9 changed files with 1061 additions and 154 deletions

View File

@@ -174,6 +174,9 @@ interface QimingclawGovernanceCommandRecord {
commandId: string;
correlationId?: string;
planId?: string | null;
taskId?: string | null;
runId?: string | null;
stepId?: string | null;
scheduleId?: string | null;
accepted?: boolean;
message?: string;
@@ -695,6 +698,20 @@ async function recordGovernanceCommand(
|| stringFromUnknown(body.context_refs?.plan_id)
|| stringFromUnknown(body.payload?.plan_id)
|| null,
taskId: stringFromUnknown(response.result?.task_id)
|| stringFromUnknown(body.context_refs?.task_id)
|| stringFromUnknown(body.payload?.task_id)
|| null,
runId: stringFromUnknown(response.result?.run_id)
|| stringFromUnknown(body.context_refs?.run_id)
|| stringFromUnknown(body.payload?.run_id)
|| stringFromUnknown(body.payload?.latest_run_id)
|| null,
stepId: stringFromUnknown(response.result?.step_id)
|| stringFromUnknown(body.context_refs?.step_id)
|| stringFromUnknown(body.payload?.step_id)
|| stringFromUnknown(body.payload?.stepId)
|| null,
scheduleId: stringFromUnknown(response.result?.schedule_id)
|| stringFromUnknown(body.context_refs?.schedule_id)
|| stringFromUnknown(body.payload?.schedule_id)
@@ -1041,12 +1058,16 @@ function latestRuntimeTime(plan: QimingclawPlanRecord, tasks: QimingclawTaskReco
function taskStatusTone(status: string): 'running' | 'success' | 'danger' | 'waiting' {
const normalized = status.toLowerCase();
if (['completed', 'succeeded', 'success', 'done'].includes(normalized)) return 'success';
if (['failed', 'error', 'rejected'].includes(normalized)) return 'danger';
if (['failed', 'error', 'rejected', 'cancelled'].includes(normalized)) return 'danger';
if (['running', 'active', 'leased', 'syncing'].includes(normalized)) return 'running';
return 'waiting';
}
function taskStatusLabel(status: string): string {
const normalized = status.toLowerCase();
if (normalized === 'skipped') return '已跳过';
if (normalized === 'cancelled') return '已取消';
if (normalized === 'paused') return '已暂停';
const tone = taskStatusTone(status);
if (tone === 'success') return '已完成';
if (tone === 'danger') return '异常';
@@ -1107,25 +1128,39 @@ function buildTaskAgents(snapshot: QimingclawSnapshot | null): DigitalEmployeeTa
return runtimeTasks(snapshot).map((task) => {
const latestRun = latestRunForTask(runs, task.id);
const tone = taskStatusTone(task.status);
const normalizedStatus = task.status.toLowerCase();
const stepId = taskStepId(task);
return {
agent_id: `task-agent-${slugifyIdPart(task.id)}`,
task_id: task.id,
plan_id: task.planId ?? null,
step_id: stepId,
title: task.title,
execution_state: task.status,
status_label: taskStatusLabel(task.status),
status_tone: tone,
assigned_skill_id: task.assignedSkillId ?? null,
latest_run_id: latestRun?.id ?? null,
can_retry: tone === 'danger',
can_skip: tone === 'danger' || tone === 'waiting',
can_cancel: tone === 'running',
next_action: tone === 'danger' ? 'retry_or_skip' : tone === 'running' ? 'watch_progress' : tone === 'success' ? 'collect_artifacts' : 'start_run',
can_retry: ['failed', 'cancelled', 'skipped'].includes(normalizedStatus),
can_skip: ['pending', 'running'].includes(normalizedStatus),
can_cancel: ['pending', 'running', 'paused'].includes(normalizedStatus),
next_action: taskAgentNextAction(normalizedStatus, tone),
updated_at: latestRun?.updatedAt ?? task.updatedAt,
};
});
}
function taskAgentNextAction(
status: string,
tone: ReturnType<typeof taskStatusTone>,
): string {
if (['failed', 'cancelled', 'skipped'].includes(status)) return 'retry_or_review';
if (status === 'paused') return 'resume_or_cancel';
if (tone === 'running') return 'watch_progress';
if (tone === 'success') return 'collect_artifacts';
return 'start_run';
}
function latestRunForTask(runs: QimingclawRunRecord[], taskId: string): QimingclawRunRecord | null {
const sorted = runs
.filter((run) => run.taskId === taskId)
@@ -2457,6 +2492,12 @@ async function handleGovernanceCommand(
const taskId = stringFromUnknown(body.context_refs?.task_id)
|| stringFromUnknown(body.payload?.task_id)
|| stringFromUnknown(body.payload?.original_task_id);
const runId = stringFromUnknown(body.context_refs?.run_id)
|| stringFromUnknown(body.payload?.run_id)
|| stringFromUnknown(body.payload?.latest_run_id);
const stepId = stringFromUnknown(body.context_refs?.step_id)
|| stringFromUnknown(body.payload?.step_id)
|| stringFromUnknown(body.payload?.stepId);
const scheduleId = stringFromUnknown(body.context_refs?.schedule_id)
|| stringFromUnknown(body.payload?.schedule_id);
@@ -2529,9 +2570,14 @@ async function handleGovernanceCommand(
if (['retry_task', 'skip_task', 'pause_task', 'resume_task', 'provide_task_input', 'cancel_task', 'start_run', 'cancel_run'].includes(body.command_kind)) {
const targetTask = taskId || runtimeTasks(snapshot)[0]?.id || 'qimingclaw-task-agent';
const runtimeTask = runtimeTasks(snapshot).find((task) => task.id === targetTask);
const targetRunId = runId || latestRunForTask(runtimeRuns(snapshot), targetTask)?.id || null;
const targetStepId = stepId || (runtimeTask ? taskStepId(runtimeTask) : null);
const response = governanceAccepted(commandId, correlationId, `已接受 ${body.command_kind},并记录到 qimingclaw Task Agent 控制平面。`, {
plan_id: planId || runtimeTasks(snapshot).find((task) => task.id === targetTask)?.planId || 'qimingclaw-task-agent-plan',
plan_id: planId || runtimeTask?.planId || 'qimingclaw-task-agent-plan',
task_id: targetTask,
run_id: targetRunId,
step_id: targetStepId,
task_agent_id: `task-agent-${slugifyIdPart(targetTask)}`,
status: taskGovernanceStatus(body.command_kind),
next_action: taskGovernanceNextAction(body.command_kind),

View File

@@ -1356,6 +1356,7 @@ export default function CommandDeck({ onNavigate }: { onNavigate: (target: Digit
plan_id: agent.plan_id,
task_id: agent.task_id,
run_id: agent.latest_run_id,
step_id: agent.step_id,
task_agent_id: agent.agent_id,
},
payload: {
@@ -1363,6 +1364,7 @@ export default function CommandDeck({ onNavigate }: { onNavigate: (target: Digit
task_agent_id: agent.agent_id,
task_id: agent.task_id,
plan_id: agent.plan_id,
step_id: agent.step_id,
latest_run_id: agent.latest_run_id,
title: agent.title,
requested_action: command,

View File

@@ -629,6 +629,7 @@ export interface DigitalEmployeeTaskAgentState {
agent_id: string;
task_id: string;
plan_id?: string | null;
step_id?: string | null;
title: string;
execution_state: string;
status_label: string;