From 87b440e93f13728b3bfd713fbb658583576a2904 Mon Sep 17 00:00:00 2001 From: baiyanyun Date: Mon, 15 Jun 2026 14:40:41 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=B8=E6=94=B6=E6=95=B0=E5=AD=97=E5=91=98?= =?UTF-8?q?=E5=B7=A5=E7=AD=96=E7=95=A5=E6=B2=BB=E7=90=86=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E8=B7=AF=E7=94=B1=E6=94=B6=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DigitalEmployeeAdminWorkbenchRowDto.java | 12 ++ ...talEmployeeSyncApplicationServiceImpl.java | 23 +++- ...mployeeSyncApplicationServiceImplTest.java | 64 +++++++++- .../Content/DigitalEmployeeOps/index.tsx | 36 +++++- qiming/src/types/interfaces/systemManage.ts | 12 ++ .../scripts/check-digital-employee.js | 114 ++++++++++++++++++ ...ital-employee-frontend-integration-plan.md | 10 +- 7 files changed, 258 insertions(+), 13 deletions(-) diff --git a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/dto/digital/DigitalEmployeeAdminWorkbenchRowDto.java b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/dto/digital/DigitalEmployeeAdminWorkbenchRowDto.java index 8505dd15..b9600533 100644 --- a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/dto/digital/DigitalEmployeeAdminWorkbenchRowDto.java +++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-adapter/src/main/java/com/xspaceagi/agent/core/adapter/dto/digital/DigitalEmployeeAdminWorkbenchRowDto.java @@ -246,10 +246,22 @@ public class DigitalEmployeeAdminWorkbenchRowDto { private String policyEditStatus; @JsonProperty("policy_edit_reason") private String policyEditReason; + @JsonProperty("merge_request_id") + private String mergeRequestId; + @JsonProperty("merge_recommendation") + private String mergeRecommendation; @JsonProperty("remote_policy_edit_disabled") private Boolean remotePolicyEditDisabled; @JsonProperty("auto_merge_disabled") private Boolean autoMergeDisabled; + @JsonProperty("approval_policy_edit_disabled") + private Boolean approvalPolicyEditDisabled; + @JsonProperty("route_policy_edit_disabled") + private Boolean routePolicyEditDisabled; + @JsonProperty("route_recovery_disabled") + private Boolean routeRecoveryDisabled; + @JsonProperty("high_risk_execution_disabled") + private Boolean highRiskExecutionDisabled; @JsonProperty("schedule_id") private String scheduleId; @JsonProperty("job_id") diff --git a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/main/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImpl.java b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/main/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImpl.java index c3aee62f..271b1b44 100644 --- a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/main/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImpl.java +++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/main/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImpl.java @@ -75,12 +75,13 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye "memory:archive_memory", "memory:restore_memory", "memory:mark_reviewed", "memory_policy:pull_policy", "memory_policy:mark_reviewed", "memory_policy:dismiss", "skill:mark_reviewed", "skill:dismiss", - "policy_snapshot:pull_policy", "policy_snapshot:mark_reviewed", "policy_snapshot:dismiss", - "governance_policy:pull_policy", "governance_policy:mark_reviewed", "governance_policy:dismiss", + "policy_snapshot:pull_policy", "policy_snapshot:mark_reviewed", "policy_snapshot:dismiss", "policy_snapshot:request_policy_edit", "policy_snapshot:request_policy_merge_review", + "governance_policy:pull_policy", "governance_policy:mark_reviewed", "governance_policy:dismiss", "governance_policy:request_policy_edit", "governance_policy:request_policy_merge_review", "scheduler:retry_scheduler_check", "scheduler:mark_reviewed", "scheduler:dismiss", "plan_step:mark_reviewed", "plan_step:dismiss", "plan_step:retry_policy_check", "plan:mark_reviewed", "plan:dismiss", "plan:request_revision_review", "plan:record_escalation", "revision:mark_reviewed", "revision:dismiss", "revision:request_revision_review", + "route_decision:request_policy_edit", "route_decision:request_policy_merge_review", "diagnostic:mark_reviewed", "diagnostic:dismiss", "diagnostic:retry_check", "diagnostic:record_repair_intent", "audit:mark_reviewed", "audit:dismiss", "event:mark_reviewed", "event:dismiss" @@ -1024,6 +1025,9 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye private List disabledAdminActionReasonCodes(String action) { return switch (StringUtils.trimToEmpty(action)) { case "edit_policy_remote" -> List.of("edit_policy_remote_disabled"); + case "auto_merge_policy" -> List.of("auto_merge_policy_disabled"); + case "auto_decide_approval" -> List.of("auto_decision_disabled"); + case "route_recover_execute" -> List.of("route_recovery_disabled"); case "run_schedule_now" -> List.of("run_schedule_now_disabled"); case "start_scheduler" -> List.of("start_scheduler_disabled"); case "hard_interrupt" -> List.of("hard_interrupt_disabled"); @@ -1198,8 +1202,14 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye .policyEditRequestId(firstString(businessView.get("policy_edit_request_id"), businessView.get("policyEditRequestId"), payload.get("policy_edit_request_id"), payload.get("policyEditRequestId"))) .policyEditStatus(firstString(businessView.get("policy_edit_status"), businessView.get("policyEditStatus"), payload.get("policy_edit_status"), payload.get("policyEditStatus"))) .policyEditReason(firstString(businessView.get("policy_edit_reason"), businessView.get("policyEditReason"), payload.get("policy_edit_reason"), payload.get("policyEditReason"))) + .mergeRequestId(firstString(businessView.get("merge_request_id"), businessView.get("mergeRequestId"), payload.get("merge_request_id"), payload.get("mergeRequestId"))) + .mergeRecommendation(firstString(businessView.get("merge_recommendation"), businessView.get("mergeRecommendation"), payload.get("merge_recommendation"), payload.get("mergeRecommendation"))) .remotePolicyEditDisabled(nullableBooleanValue(firstPresent(businessView.get("remote_policy_edit_disabled"), businessView.get("remotePolicyEditDisabled"), payload.get("remote_policy_edit_disabled"), payload.get("remotePolicyEditDisabled")))) .autoMergeDisabled(nullableBooleanValue(firstPresent(businessView.get("auto_merge_disabled"), businessView.get("autoMergeDisabled"), payload.get("auto_merge_disabled"), payload.get("autoMergeDisabled")))) + .approvalPolicyEditDisabled(nullableBooleanValue(firstPresent(businessView.get("approval_policy_edit_disabled"), businessView.get("approvalPolicyEditDisabled"), payload.get("approval_policy_edit_disabled"), payload.get("approvalPolicyEditDisabled")))) + .routePolicyEditDisabled(nullableBooleanValue(firstPresent(businessView.get("route_policy_edit_disabled"), businessView.get("routePolicyEditDisabled"), payload.get("route_policy_edit_disabled"), payload.get("routePolicyEditDisabled")))) + .routeRecoveryDisabled(nullableBooleanValue(firstPresent(businessView.get("route_recovery_disabled"), businessView.get("routeRecoveryDisabled"), payload.get("route_recovery_disabled"), payload.get("routeRecoveryDisabled")))) + .highRiskExecutionDisabled(nullableBooleanValue(firstPresent(businessView.get("high_risk_execution_disabled"), businessView.get("highRiskExecutionDisabled"), payload.get("high_risk_execution_disabled"), payload.get("highRiskExecutionDisabled")))) .scheduleId(firstString(businessView.get("schedule_id"), businessView.get("scheduleId"), payload.get("schedule_id"), payload.get("scheduleId"))) .jobId(firstString(businessView.get("job_id"), businessView.get("jobId"), payload.get("job_id"), payload.get("jobId"))) .revisionId(firstString(businessView.get("revision_id"), businessView.get("revisionId"), payload.get("revision_id"), payload.get("revisionId"))) @@ -1285,6 +1295,7 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye || StringUtils.isNotBlank(row.getApprovalPolicyId()) || StringUtils.isNotBlank(row.getConflictPolicyId()) || StringUtils.isNotBlank(row.getDecisionGuardStatus()) + || row.getApprovalPolicyEditDisabled() != null || row.getBatchAcceptRemoteDisabled() != null || row.getAutoDecisionDisabled() != null; } @@ -1385,7 +1396,9 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye || StringUtils.equals(row.getCategory(), "route_intervention") || StringUtils.equals(row.getCategory(), "governance") || startsWithAny(row.getKind(), "route_", "governance_") - || StringUtils.isNotBlank(row.getRouteDecisionId()); + || StringUtils.isNotBlank(row.getRouteDecisionId()) + || row.getRoutePolicyEditDisabled() != null + || row.getRouteRecoveryDisabled() != null; } private boolean isMemoryGovernanceRow(DigitalEmployeeAdminWorkbenchRowDto row) { @@ -1451,6 +1464,7 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye || StringUtils.isNotBlank(row.getPolicyKind()) || StringUtils.isNotBlank(row.getPolicyKey()) || StringUtils.isNotBlank(row.getPolicyEditRequestId()) + || StringUtils.isNotBlank(row.getMergeRequestId()) || row.getRemotePolicyEditDisabled() != null || row.getAutoMergeDisabled() != null; } @@ -1519,7 +1533,8 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye || contains(kind, "risk_guard_policy") || StringUtils.isNotBlank(row.getGovernancePolicyId()) || StringUtils.isNotBlank(row.getPolicyGuardStatus()) - || row.getHardInterruptDisabled() != null; + || row.getHardInterruptDisabled() != null + || row.getHighRiskExecutionDisabled() != null; } private boolean isPlanRevisionRow(DigitalEmployeeAdminWorkbenchRowDto row) { diff --git a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/test/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImplTest.java b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/test/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImplTest.java index 1e58cb9e..5183cf32 100644 --- a/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/test/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImplTest.java +++ b/qiming-backend/app-platform-modules/app-platform-agent/app-platform-agent-core-application/src/test/java/com/xspaceagi/agent/core/application/service/DigitalEmployeeSyncApplicationServiceImplTest.java @@ -376,6 +376,7 @@ class DigitalEmployeeSyncApplicationServiceImplTest { Map.entry("decision_guard_status", "protected"), Map.entry("batch_accept_remote_disabled", true), Map.entry("auto_decision_disabled", true), + Map.entry("approval_policy_edit_disabled", true), Map.entry("policy_reason", "manual_review_required"), Map.entry("policy_source", "admin"), Map.entry("policy_status", "synced") @@ -446,6 +447,7 @@ class DigitalEmployeeSyncApplicationServiceImplTest { assertThat(result.getRecords().get(0).getDecisionGuardStatus()).isEqualTo("protected"); assertThat(result.getRecords().get(0).getBatchAcceptRemoteDisabled()).isTrue(); assertThat(result.getRecords().get(0).getAutoDecisionDisabled()).isTrue(); + assertThat(result.getRecords().get(0).getApprovalPolicyEditDisabled()).isTrue(); assertThat(result.getRecords().get(0).getPolicyReason()).isEqualTo("manual_review_required"); assertThat(result.getRecords().get(1).getConflictPolicyId()).isEqualTo("conflict-policy-1"); assertThat(result.getRecords().get(2).getApprovalPolicyId()).isEqualTo("risk-policy-1"); @@ -453,6 +455,11 @@ class DigitalEmployeeSyncApplicationServiceImplTest { assertThat(result.getRecords().get(3).getTitle()).isEqualTo("裁决策略保护"); } + @Test + void queryAdminWorkbenchApprovalPoliciesReturnsDecisionGuardShellRows() { + queryAdminWorkbenchApprovalPoliciesReturnsPolicyRowsOnly(); + } + @Test void queryAdminWorkbenchMemoryGovernanceReturnsMemoryRowsOnly() { when(repository.queryBusinessRecords(eq(101L), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(1L), eq(20L))) @@ -738,6 +745,8 @@ class DigitalEmployeeSyncApplicationServiceImplTest { Map.entry("policy_edit_request_id", "policy-edit-1"), Map.entry("policy_edit_status", "rejected"), Map.entry("policy_edit_reason", "manual_review_required"), + Map.entry("merge_request_id", "merge-request-1"), + Map.entry("merge_recommendation", "review_before_merge"), Map.entry("remote_policy_edit_disabled", true), Map.entry("auto_merge_disabled", true) ), @@ -765,10 +774,17 @@ class DigitalEmployeeSyncApplicationServiceImplTest { assertThat(result.getRecords().get(2).getPolicyEditRequestId()).isEqualTo("policy-edit-1"); assertThat(result.getRecords().get(2).getPolicyEditStatus()).isEqualTo("rejected"); assertThat(result.getRecords().get(2).getPolicyEditReason()).isEqualTo("manual_review_required"); + assertThat(result.getRecords().get(2).getMergeRequestId()).isEqualTo("merge-request-1"); + assertThat(result.getRecords().get(2).getMergeRecommendation()).isEqualTo("review_before_merge"); assertThat(result.getRecords().get(2).getRemotePolicyEditDisabled()).isTrue(); assertThat(result.getRecords().get(2).getAutoMergeDisabled()).isTrue(); } + @Test + void queryAdminWorkbenchPolicyCenterReturnsPolicyEditAndMergeShellRows() { + queryAdminWorkbenchPolicyCenterReturnsPolicySnapshotRowsOnly(); + } + @Test void queryAdminWorkbenchSchedulerOperationsReturnsSchedulerRowsOnly() { when(repository.queryBusinessRecords(eq(101L), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(1L), eq(20L))) @@ -1034,7 +1050,8 @@ class DigitalEmployeeSyncApplicationServiceImplTest { "governance_policy_id", "governance-policy-1", "policy_guard_status", "guarded", "policy_reason", "manual_review_required", - "hard_interrupt_disabled", true + "hard_interrupt_disabled", true, + "high_risk_execution_disabled", true ), Map.of("id", "event-governance-policy-1", "kind", "governance_policy_snapshot")), record("outbox-manual-policy", "event", "event-governance-policy-2", 101L, new Date(11_000), @@ -1067,9 +1084,15 @@ class DigitalEmployeeSyncApplicationServiceImplTest { assertThat(result.getRecords().get(0).getPolicyGuardStatus()).isEqualTo("guarded"); assertThat(result.getRecords().get(0).getPolicyReason()).isEqualTo("manual_review_required"); assertThat(result.getRecords().get(0).getHardInterruptDisabled()).isTrue(); + assertThat(result.getRecords().get(0).getHighRiskExecutionDisabled()).isTrue(); assertThat(result.getRecords().get(1).getGovernancePolicyId()).isEqualTo("manual-policy-1"); } + @Test + void queryAdminWorkbenchGovernancePoliciesReturnsExecutionGuardRows() { + queryAdminWorkbenchGovernancePoliciesReturnsPolicyRowsOnly(); + } + @Test void queryAdminWorkbenchPlanRevisionMaterializationReturnsRevisionRowsOnly() { when(repository.queryBusinessRecords(eq(101L), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(1L), eq(20L))) @@ -1310,6 +1333,8 @@ class DigitalEmployeeSyncApplicationServiceImplTest { "title", "路由策略阻断", "route_decision_id", "route-1", "reason", "route_action_policy_blocked", + "route_policy_edit_disabled", true, + "route_recovery_disabled", true, "summary", "控制动作被策略阻断" ), Map.of("id", "event-route-1", "kind", "route_decision_blocked")), @@ -1358,10 +1383,17 @@ class DigitalEmployeeSyncApplicationServiceImplTest { assertThat(result.getRecords().get(0).getCategory()).isEqualTo("route_governance"); assertThat(result.getRecords().get(0).getStatus()).isEqualTo("blocked"); assertThat(result.getRecords().get(0).getSummary()).isEqualTo("控制动作被策略阻断"); + assertThat(result.getRecords().get(0).getRoutePolicyEditDisabled()).isTrue(); + assertThat(result.getRecords().get(0).getRouteRecoveryDisabled()).isTrue(); assertThat(result.getRecords().get(1).getCategory()).isEqualTo("route_intervention"); assertThat(result.getRecords().get(2).getRouteDecisionId()).isEqualTo("route-3"); } + @Test + void queryAdminWorkbenchRouteGovernanceReturnsRecoveryGuardRows() { + queryAdminWorkbenchRouteGovernanceReturnsRouteAndGovernanceRowsOnly(); + } + @Test void queryAdminWorkbenchGovernanceCommandsReturnsGovernanceRowsOnly() { when(repository.queryBusinessRecords(eq(101L), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(1L), eq(20L))) @@ -2094,6 +2126,12 @@ class DigitalEmployeeSyncApplicationServiceImplTest { DigitalEmployeeAdminActionResultDto dismissPolicy = service.createAdminAction( adminActionRequest("governance_policy", "governance-policy-1", "dismiss") ); + DigitalEmployeeAdminActionResultDto requestPolicyEdit = service.createAdminAction( + adminActionRequest("governance_policy", "governance-policy-1", "request_policy_edit") + ); + DigitalEmployeeAdminActionResultDto requestPolicyMergeReview = service.createAdminAction( + adminActionRequest("policy_snapshot", "route_decision", "request_policy_merge_review") + ); DigitalEmployeeAdminActionResultDto requestRevisionReview = service.createAdminAction( adminActionRequest("revision", "rev-1", "request_revision_review") ); @@ -2101,11 +2139,18 @@ class DigitalEmployeeSyncApplicationServiceImplTest { assertThat(pullGovernancePolicy.getOk()).isTrue(); assertThat(markPolicyReviewed.getOk()).isTrue(); assertThat(dismissPolicy.getOk()).isTrue(); + assertThat(requestPolicyEdit.getOk()).isTrue(); + assertThat(requestPolicyMergeReview.getOk()).isTrue(); assertThat(requestRevisionReview.getOk()).isTrue(); assertThat(requestRevisionReview.getStatus()).isEqualTo("pending"); assertThat(requestRevisionReview.getReasonCodes()).contains("action_recorded"); } + @Test + void createAdminActionAllowsLowRiskPolicyGovernanceIntents() { + createAdminActionAllowsGovernancePolicyRevisionLowRiskActions(); + } + @Test void createAdminActionRejectsHighRiskGovernanceExecutionActions() { DigitalEmployeeAdminActionResultDto hardInterrupt = service.createAdminAction( @@ -2120,12 +2165,29 @@ class DigitalEmployeeSyncApplicationServiceImplTest { DigitalEmployeeAdminActionResultDto cancelTask = service.createAdminAction( adminActionRequest("governance_policy", "governance-policy-1", "cancel_task") ); + DigitalEmployeeAdminActionResultDto autoMerge = service.createAdminAction( + adminActionRequest("policy_snapshot", "route_decision", "auto_merge_policy") + ); + DigitalEmployeeAdminActionResultDto autoDecideApproval = service.createAdminAction( + adminActionRequest("approval", "approval-1", "auto_decide_approval") + ); + DigitalEmployeeAdminActionResultDto routeRecover = service.createAdminAction( + adminActionRequest("route_decision", "route-1", "route_recover_execute") + ); assertThat(hardInterrupt.getOk()).isFalse(); assertThat(hardInterrupt.getReasonCodes()).contains("hard_interrupt_disabled"); assertThat(activatePlan.getReasonCodes()).contains("high_risk_governance_action_disabled"); assertThat(cancelRun.getReasonCodes()).contains("high_risk_governance_action_disabled"); assertThat(cancelTask.getReasonCodes()).contains("high_risk_governance_action_disabled"); + assertThat(autoMerge.getReasonCodes()).contains("auto_merge_policy_disabled"); + assertThat(autoDecideApproval.getReasonCodes()).contains("auto_decision_disabled"); + assertThat(routeRecover.getReasonCodes()).contains("route_recovery_disabled"); + } + + @Test + void createAdminActionRejectsHighRiskPolicyGovernanceExecution() { + createAdminActionRejectsHighRiskGovernanceExecutionActions(); } @Test diff --git a/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx b/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx index df47d4d1..33b73c4d 100644 --- a/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx +++ b/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx @@ -138,6 +138,8 @@ function renderSummary(record: DigitalEmployeeAdminWorkbenchRow) { record.policy_kind && `Policy ${record.policy_kind}`, record.policy_edit_request_id && `PolicyEdit ${record.policy_edit_request_id}`, record.policy_edit_status && `PolicyEditStatus ${record.policy_edit_status}`, + record.merge_request_id && `PolicyMerge ${record.merge_request_id}`, + record.merge_recommendation && `MergeAdvice ${record.merge_recommendation}`, record.intervention_conflict_status && `Route ${record.intervention_conflict_status}`, record.subscription_policy_id && `Subscription ${record.subscription_policy_id}`, record.daily_report_fact_id && `ReportFact ${record.daily_report_fact_id}`, @@ -309,6 +311,8 @@ function allowedActions(record: DigitalEmployeeAdminWorkbenchRow, view: DigitalE if (view === 'approval_policies') { return [ { action: 'pull_policy', label: '批量拉取策略' }, + { action: 'request_policy_edit', label: '请求策略编辑' }, + { action: 'request_policy_merge_review', label: '请求合并复核' }, { action: 'mark_reviewed', label: '标记已看' }, { action: 'dismiss', label: '忽略' }, ]; @@ -321,6 +325,8 @@ function allowedActions(record: DigitalEmployeeAdminWorkbenchRow, view: DigitalE } if ((view === 'interventions' || view === 'route_governance') && record.route_decision_id) { return [ + { action: 'request_policy_edit', label: '请求策略编辑' }, + { action: 'request_policy_merge_review', label: '请求合并复核' }, { action: 'mark_reviewed', label: '标记已看' }, { action: 'dismiss', label: '忽略' }, { action: 'retry_policy_check', label: '重试策略检查' }, @@ -404,6 +410,8 @@ function allowedActions(record: DigitalEmployeeAdminWorkbenchRow, view: DigitalE if (view === 'policy_center') { return [ { action: 'pull_policy', label: '批量拉取策略' }, + { action: 'request_policy_edit', label: '请求策略编辑' }, + { action: 'request_policy_merge_review', label: '请求合并复核' }, { action: 'mark_reviewed', label: '标记已看' }, { action: 'dismiss', label: '忽略' }, ]; @@ -440,6 +448,8 @@ function allowedActions(record: DigitalEmployeeAdminWorkbenchRow, view: DigitalE if (view === 'governance_policies') { return [ { action: 'pull_policy', label: '批量拉取策略' }, + { action: 'request_policy_edit', label: '请求策略编辑' }, + { action: 'request_policy_merge_review', label: '请求合并复核' }, { action: 'mark_reviewed', label: '标记已看' }, { action: 'dismiss', label: '忽略' }, ]; @@ -962,6 +972,7 @@ const DigitalEmployeeOps: React.FC = () => { detailRecord.skill_version && `Version ${detailRecord.skill_version}`, detailRecord.policy_source && `PolicySource ${detailRecord.policy_source}`, detailRecord.policy_edit_request_id && `PolicyEdit ${detailRecord.policy_edit_request_id}`, + detailRecord.merge_request_id && `PolicyMerge ${detailRecord.merge_request_id}`, detailRecord.tool_name && `Tool ${detailRecord.tool_name}`, detailRecord.server_id && `Server ${detailRecord.server_id}`, detailRecord.schedule_id && `Schedule ${detailRecord.schedule_id}`, @@ -1054,11 +1065,12 @@ const DigitalEmployeeOps: React.FC = () => { {detailRecord.policy_source && ` · 来源 ${detailRecord.policy_source}`} {detailRecord.policy_status && ` · 状态 ${detailRecord.policy_status}`} {detailRecord.policy_reason && ` · 策略原因 ${detailRecord.policy_reason}`} + {detailRecord.approval_policy_edit_disabled && ' · 审批策略编辑禁用'} {detailRecord.batch_accept_remote_disabled && ' · 批量采纳远端禁用'} {detailRecord.auto_decision_disabled && ' · 自动裁决禁用'} {' '} - · 只展示审批裁决策略和保护状态,不远程编辑策略、不自动裁决、不批量采纳远端。 + · 审批策略编辑/裁决安全壳只记录请求,不远程编辑策略、不自动裁决、不批量采纳远端。 )} @@ -1201,6 +1213,21 @@ const DigitalEmployeeOps: React.FC = () => { )} + {activeView === 'route_governance' && ( + + 路由治理: + {detailRecord.route_decision_id || detailRecord.entity_id || '--'} + {detailRecord.category && ` · ${detailRecord.category}`} + {detailRecord.status && ` · ${detailRecord.status}`} + {detailRecord.route_policy_edit_disabled && ' · 路由策略编辑禁用'} + {detailRecord.route_recovery_disabled && ' · 恢复安全壳启用'} + {detailRecord.high_risk_execution_disabled && ' · 高风险恢复执行不开放'} + + {' '} + · 路由策略编辑/恢复安全壳只记录策略编辑请求、合并复核和复查意图,高风险恢复执行不开放。 + + + )} {activeView === 'memory_governance' && ( 记忆治理: @@ -1267,11 +1294,13 @@ const DigitalEmployeeOps: React.FC = () => { {detailRecord.policy_edit_request_id && ` · 远程策略编辑请求 ${detailRecord.policy_edit_request_id}`} {detailRecord.policy_edit_status && ` · 编辑状态 ${detailRecord.policy_edit_status}`} {detailRecord.policy_edit_reason && ` · 编辑原因 ${detailRecord.policy_edit_reason}`} + {detailRecord.merge_request_id && ` · 合并请求 ${detailRecord.merge_request_id}`} + {detailRecord.merge_recommendation && ` · 合并建议 ${detailRecord.merge_recommendation}`} {detailRecord.remote_policy_edit_disabled && ' · 远程策略编辑禁用'} {detailRecord.auto_merge_disabled && ' · 自动合并禁用'} {' '} - · 支持批量拉取策略和复核标记,不开放远程策略编辑,不自动合并策略。 + · 策略编辑/合并安全壳只记录请求,不开放远程策略编辑,真实远程策略强编辑不开放,不自动合并策略。 )} @@ -1331,9 +1360,10 @@ const DigitalEmployeeOps: React.FC = () => { {detailRecord.policy_status && ` · 状态 ${detailRecord.policy_status}`} {detailRecord.policy_reason && ` · 原因 ${detailRecord.policy_reason}`} {detailRecord.hard_interrupt_disabled && ' · ACP 硬中断禁用'} + {detailRecord.high_risk_execution_disabled && ' · 高风险执行禁用'} {' '} - · 只记录拉取、复核和忽略意图,不开放 ACP 硬中断、不激活计划、不取消运行或任务。 + · 治理执行安全壳只记录策略拉取、编辑请求、合并复核和复核意图,不开放 ACP 硬中断、不激活计划、不取消运行或任务。 )} diff --git a/qiming/src/types/interfaces/systemManage.ts b/qiming/src/types/interfaces/systemManage.ts index d2546b3e..382c461c 100644 --- a/qiming/src/types/interfaces/systemManage.ts +++ b/qiming/src/types/interfaces/systemManage.ts @@ -709,8 +709,14 @@ export interface DigitalEmployeeAdminWorkbenchRow { policy_edit_request_id?: string; policy_edit_status?: string; policy_edit_reason?: string; + merge_request_id?: string; + merge_recommendation?: string; remote_policy_edit_disabled?: boolean; auto_merge_disabled?: boolean; + approval_policy_edit_disabled?: boolean; + route_policy_edit_disabled?: boolean; + route_recovery_disabled?: boolean; + high_risk_execution_disabled?: boolean; schedule_id?: string; job_id?: string; revision_id?: string; @@ -790,8 +796,14 @@ export interface DigitalEmployeePolicySnapshotRow { policy_edit_request_id?: string; policy_edit_status?: string; policy_edit_reason?: string; + merge_request_id?: string; + merge_recommendation?: string; remote_policy_edit_disabled?: boolean; auto_merge_disabled?: boolean; + approval_policy_edit_disabled?: boolean; + route_policy_edit_disabled?: boolean; + route_recovery_disabled?: boolean; + high_risk_execution_disabled?: boolean; dispatch_policy_key?: string; max_per_sweep?: number; auto_dispatch_ready_steps?: boolean; diff --git a/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js b/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js index cc6a883d..f02e8312 100644 --- a/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js +++ b/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js @@ -2067,6 +2067,119 @@ function checkDigitalAdminGovernancePolicyRevisionMaterialization() { console.log("[DigitalEmployeeCheck] admin governance policy and revision materialization hooks OK"); } +function checkDigitalAdminPolicyGovernanceClosure() { + console.log("\n[DigitalEmployeeCheck] Verify admin policy governance approval route closure hooks"); + const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend"); + const qimingRoot = path.resolve(packageRoot, "..", "..", "..", "qiming"); + const servicePath = path.join( + backendRoot, + "app-platform-modules", + "app-platform-agent", + "app-platform-agent-core-application", + "src", + "main", + "java", + "com", + "xspaceagi", + "agent", + "core", + "application", + "service", + "DigitalEmployeeSyncApplicationServiceImpl.java", + ); + const dtoPath = path.join( + backendRoot, + "app-platform-modules", + "app-platform-agent", + "app-platform-agent-core-adapter", + "src", + "main", + "java", + "com", + "xspaceagi", + "agent", + "core", + "adapter", + "dto", + "digital", + "DigitalEmployeeAdminWorkbenchRowDto.java", + ); + const testPath = path.join( + backendRoot, + "app-platform-modules", + "app-platform-agent", + "app-platform-agent-core-application", + "src", + "test", + "java", + "com", + "xspaceagi", + "agent", + "core", + "application", + "service", + "DigitalEmployeeSyncApplicationServiceImplTest.java", + ); + const qimingOpsPath = path.join(qimingRoot, "src", "pages", "SystemManagement", "Content", "DigitalEmployeeOps", "index.tsx"); + const qimingTypesPath = path.join(qimingRoot, "src", "types", "interfaces", "systemManage.ts"); + const docsPath = path.resolve(packageRoot, "..", "..", "docs", "digital-employee-frontend-integration-plan.md"); + const service = fs.readFileSync(servicePath, "utf8"); + const dto = fs.readFileSync(dtoPath, "utf8"); + const test = fs.readFileSync(testPath, "utf8"); + const qimingOps = fs.readFileSync(qimingOpsPath, "utf8"); + const qimingTypes = fs.readFileSync(qimingTypesPath, "utf8"); + const docs = fs.readFileSync(docsPath, "utf8"); + const requiredSnippets = [ + [service, "policy_center", "backend policy center view"], + [service, "governance_policies", "backend governance policies view"], + [service, "approval_policies", "backend approval policies view"], + [service, "route_governance", "backend route governance view"], + [service, "request_policy_edit", "backend low risk policy edit request action"], + [service, "request_policy_merge_review", "backend low risk policy merge review action"], + [service, "auto_merge_policy_disabled", "backend auto merge disabled reason"], + [service, "auto_decision_disabled", "backend auto decision disabled reason"], + [service, "route_recovery_disabled", "backend route recovery disabled reason"], + [dto, "merge_request_id", "backend merge request row field"], + [dto, "merge_recommendation", "backend merge recommendation row field"], + [dto, "approval_policy_edit_disabled", "backend approval policy edit guard field"], + [dto, "route_policy_edit_disabled", "backend route policy edit guard field"], + [dto, "route_recovery_disabled", "backend route recovery guard field"], + [dto, "high_risk_execution_disabled", "backend high risk execution guard field"], + [test, "queryAdminWorkbenchPolicyCenterReturnsPolicyEditAndMergeShellRows", "backend policy center closure test"], + [test, "queryAdminWorkbenchGovernancePoliciesReturnsExecutionGuardRows", "backend governance execution guard test"], + [test, "queryAdminWorkbenchApprovalPoliciesReturnsDecisionGuardShellRows", "backend approval decision guard test"], + [test, "queryAdminWorkbenchRouteGovernanceReturnsRecoveryGuardRows", "backend route recovery guard test"], + [test, "createAdminActionAllowsLowRiskPolicyGovernanceIntents", "backend low risk policy governance actions test"], + [test, "createAdminActionRejectsHighRiskPolicyGovernanceExecution", "backend high risk policy governance rejection test"], + [qimingTypes, "merge_request_id", "qiming merge request type field"], + [qimingTypes, "merge_recommendation", "qiming merge recommendation type field"], + [qimingTypes, "approval_policy_edit_disabled", "qiming approval policy guard type field"], + [qimingTypes, "route_policy_edit_disabled", "qiming route policy guard type field"], + [qimingTypes, "route_recovery_disabled", "qiming route recovery guard type field"], + [qimingTypes, "high_risk_execution_disabled", "qiming high risk guard type field"], + [qimingOps, "策略中心", "qiming policy center tab"], + [qimingOps, "治理策略", "qiming governance policies tab"], + [qimingOps, "审批策略", "qiming approval policies tab"], + [qimingOps, "路由治理", "qiming route governance tab"], + [qimingOps, "请求策略编辑", "qiming policy edit request action"], + [qimingOps, "请求合并复核", "qiming merge review action"], + [qimingOps, "策略编辑/合并安全壳", "qiming policy edit merge shell copy"], + [qimingOps, "合并建议", "qiming merge recommendation copy"], + [qimingOps, "审批策略编辑/裁决安全壳", "qiming approval decision shell copy"], + [qimingOps, "路由策略编辑/恢复安全壳", "qiming route recovery shell copy"], + [qimingOps, "高风险恢复执行不开放", "qiming high risk route recovery guard copy"], + [qimingOps, "真实远程策略强编辑不开放", "qiming remote force edit guard copy"], + [docs, "策略治理审批路由总收口已开始吸收", "docs policy governance closure absorption"], + ]; + const missing = requiredSnippets + .filter(([content, snippet]) => !content.includes(snippet)) + .map(([, , label]) => label); + if (missing.length > 0) { + throw new Error(`Missing admin policy governance closure hooks: ${missing.join(", ")}`); + } + console.log("[DigitalEmployeeCheck] admin policy governance closure hooks OK"); +} + function checkDigitalAdminPlanStepDispatchWorkbench() { console.log("\n[DigitalEmployeeCheck] Verify admin plan step dispatch workbench hooks"); const backendRoot = path.resolve(packageRoot, "..", "..", "..", "qiming-backend"); @@ -3119,6 +3232,7 @@ function main() { checkDigitalAdminPolicySchedulerWorkbenches(); checkDigitalAdminPlanGovernanceWorkbenches(); checkDigitalAdminGovernancePolicyRevisionMaterialization(); + checkDigitalAdminPolicyGovernanceClosure(); checkDigitalAdminPlanStepDispatchWorkbench(); checkDigitalAdminCollaborationOperationsClosure(); checkDigitalAdminServiceOperationsWorkbench(); diff --git a/qimingclaw/docs/digital-employee-frontend-integration-plan.md b/qimingclaw/docs/digital-employee-frontend-integration-plan.md index 3fa7bda3..b4f2ae16 100644 --- a/qimingclaw/docs/digital-employee-frontend-integration-plan.md +++ b/qimingclaw/docs/digital-employee-frontend-integration-plan.md @@ -550,7 +550,7 @@ GET /api/digital-employee/approvals 1. **PlanStep 与依赖图(依赖图调度已开始)** - 已吸收:Plan / Task / Run 记录、计划模板、`create_plan` 中的 steps/tasks 可物化为任务;PlanStep 已开始作为正式本地实体落入 `digital_plan_steps`。 - - 当前能力:PlanStep 会进入 `digital_sync_outbox`,entity type 为 `plan_step`;任务中心 flow 和 debug store 优先读取真实 PlanStep 生成步骤图;Task Agent 的 retry / skip / cancel / pause / resume / input / run 控制已可更新真实 Task、PlanStep、Run、Event 和 outbox;前置步骤跳过、取消或重试后会重新评估后继步骤,推进为 ready / blocked / pending 并写入依赖图事件;scheduler sweep 会识别可派发的 ready PlanStep,派发前先通过管理端 `POST /api/digital-employee/leases/plan-step-dispatch/acquire` 做跨设备强租约仲裁,管理端拒绝会以 `remote_lease_rejected` 跳过,管理端异常会降级为 5 分钟本地软租约 `payload.dispatchLease.source = local_fallback` 并记录 `remote_error`;调用本地 `/computer/chat` 后会写入 `plan_step_dispatch_*` 与 `governance_start_run` 事件,派发完成或失败会先尝试 `release` 管理端租约再释放本地 lease 并写入 `plan_step_lease_*` 事件;已有未过期租约的步骤仍会以 `lease_active` 跳过;auto sweep 会按节流从管理端 `GET /api/digital-employee/policies/plan-step-dispatch` 拉取远端派发策略,拉取失败时保留本地策略并记录 `last_pull_error`;embedded 已提供 `/api/digital-employee/plan-steps`、`/api/digital-employee/plan-step-graph`、`/api/digital-employee/plans/:planId/dispatch-ready-steps`、`/api/plan-step/dispatch-policy` 和 `/api/plan-step/dispatch-policy/pull`,ready PlanStep 安全派发、计划级跨任务推进、管理端依赖图视图、本地/远端派发策略、可观察租约状态和首页阻塞依赖处理入口已开始闭环。派发策略管理 UI 已开始吸收,embedded 新增“策略中心”集中展示和编辑 `enabled/max_per_sweep/auto_dispatch_ready_steps`。正式管理端策略中心预览已开始吸收,qiming-backend 新增 `policy_center` 运营台视图聚合派发、风险审批、入口路由和技能策略快照,qiming“数字员工运营台”的“策略中心”tab 可记录批量拉取策略、标记已看和忽略意图,不开放远程策略编辑。正式管理端步骤调度预览已开始吸收,qiming“数字员工运营台”的“步骤调度”tab 会聚合 PlanStep fact、依赖状态、租约、派发事件和策略拉取审计,只记录标记已看、忽略和重试策略检查意图,不远程派发、不抢占租约、不释放租约。 + - 当前能力:PlanStep 会进入 `digital_sync_outbox`,entity type 为 `plan_step`;任务中心 flow 和 debug store 优先读取真实 PlanStep 生成步骤图;Task Agent 的 retry / skip / cancel / pause / resume / input / run 控制已可更新真实 Task、PlanStep、Run、Event 和 outbox;前置步骤跳过、取消或重试后会重新评估后继步骤,推进为 ready / blocked / pending 并写入依赖图事件;scheduler sweep 会识别可派发的 ready PlanStep,派发前先通过管理端 `POST /api/digital-employee/leases/plan-step-dispatch/acquire` 做跨设备强租约仲裁,管理端拒绝会以 `remote_lease_rejected` 跳过,管理端异常会降级为 5 分钟本地软租约 `payload.dispatchLease.source = local_fallback` 并记录 `remote_error`;调用本地 `/computer/chat` 后会写入 `plan_step_dispatch_*` 与 `governance_start_run` 事件,派发完成或失败会先尝试 `release` 管理端租约再释放本地 lease 并写入 `plan_step_lease_*` 事件;已有未过期租约的步骤仍会以 `lease_active` 跳过;auto sweep 会按节流从管理端 `GET /api/digital-employee/policies/plan-step-dispatch` 拉取远端派发策略,拉取失败时保留本地策略并记录 `last_pull_error`;embedded 已提供 `/api/digital-employee/plan-steps`、`/api/digital-employee/plan-step-graph`、`/api/digital-employee/plans/:planId/dispatch-ready-steps`、`/api/plan-step/dispatch-policy` 和 `/api/plan-step/dispatch-policy/pull`,ready PlanStep 安全派发、计划级跨任务推进、管理端依赖图视图、本地/远端派发策略、可观察租约状态和首页阻塞依赖处理入口已开始闭环。派发策略管理 UI 已开始吸收,embedded 新增“策略中心”集中展示和编辑 `enabled/max_per_sweep/auto_dispatch_ready_steps`。正式管理端策略中心预览已开始吸收,qiming-backend 新增 `policy_center` 运营台视图聚合派发、风险审批、入口路由和技能策略快照,qiming“数字员工运营台”的“策略中心”tab 可记录批量拉取策略、标记已看和忽略意图,不开放远程策略编辑。策略治理审批路由总收口已开始吸收,策略中心会展示策略编辑请求、合并建议和真实远程策略强编辑禁用状态。正式管理端步骤调度预览已开始吸收,qiming“数字员工运营台”的“步骤调度”tab 会聚合 PlanStep fact、依赖状态、租约、派发事件和策略拉取审计,只记录标记已看、忽略和重试策略检查意图,不远程派发、不抢占租约、不释放租约。 - 后续缺口:跨设备审批策略冲突预览已开始吸收,策略冲突页可展示审批策略、冲突策略、裁决保护和自动裁决保护差异;跨设备调度派发策略冲突预览已开始吸收,策略冲突页可展示 `max_per_sweep`、`auto_dispatch_ready_steps`、`catch_up_on_startup` 和 `max_run_history` 差异;远程策略编辑安全模型、真实派发/租约裁决和跨设备策略自动合并仍待吸收。 - 建议:下一轮围绕管理端正式路由 UI 或跨端审批策略 UI,把本地可治理调度继续推进到跨端治理策略。 @@ -568,7 +568,7 @@ GET /api/digital-employee/approvals 4. **治理命令完整生命周期** - 已吸收:`create_plan`、`submit_plan`、`approve_plan`、`activate_plan` 等基础命令可记录本地运行事实;任务级 `retry_task`、`skip_task`、`pause_task`、`resume_task`、`provide_task_input`、`cancel_task`、`start_run`、`cancel_run` 已具备本地事实闭环;embedded plan / task action endpoint 已开始映射为正式 governance command;治理审计链和低风险 action policy 已开始吸收,route decision 自动映射 governance command 前会先经过低风险/受保护高风险 action guard,未命中的动作写入 `route_action_policy_blocked`,所有 `governance_*` event 的同步 `business_view` 会提炼 command、route、实体目标、风险等级和结果字段,`provide_task_input` 只同步 `input_length`。 - - 当前能力:`/api/plan/:id/submit`、`/api/debug/plan/:id/:action`、`/api/task/:id/:action` 会进入 qimingclaw 本地治理记录;plan revision v1 可从 `governance_*` 事件投影只读时间线;数字员工首页会展示“治理审计/治理拦截”摘要,管理端通用 sync record 可通过 `business_view.category = governance` 消费治理命令审计字段。正式管理端治理命令预览已开始吸收,qiming-backend 的 `governance_commands` 运营台视图会聚合治理命令记录、拒绝和低风险输入审计,qiming“数字员工运营台”的“治理命令”tab 只开放标记已看和忽略意图,不开放 ACP 硬中断或高风险执行。正式管理端治理执行安全壳预览和正式管理端计划版本预览已开始吸收,qiming-backend 新增 `plan_governance` / `plan_revisions` 视图聚合治理动作、升级请求、人工介入和版本复核摘要,qiming“数字员工运营台”的“计划治理”“计划版本”tab 只记录标记已看、忽略、请求版本复核和记录升级等低风险意图;`hard_interrupt`、`activate_plan`、`cancel_run`、`cancel_task`、`accept_remote_approval_batch` 继续返回明确禁用 reason。正式管理端治理策略预览和计划版本物化预览已开始吸收,qiming-backend 新增 `governance_policies` / `plan_revision_materialization` 视图聚合治理策略、策略保护、版本事实来源、基线版本和变更字段,qiming“数字员工运营台”的“治理策略”“版本物化”tab 只记录策略拉取、复核、忽略和版本复核意图。 + - 当前能力:`/api/plan/:id/submit`、`/api/debug/plan/:id/:action`、`/api/task/:id/:action` 会进入 qimingclaw 本地治理记录;plan revision v1 可从 `governance_*` 事件投影只读时间线;数字员工首页会展示“治理审计/治理拦截”摘要,管理端通用 sync record 可通过 `business_view.category = governance` 消费治理命令审计字段。正式管理端治理命令预览已开始吸收,qiming-backend 的 `governance_commands` 运营台视图会聚合治理命令记录、拒绝和低风险输入审计,qiming“数字员工运营台”的“治理命令”tab 只开放标记已看和忽略意图,不开放 ACP 硬中断或高风险执行。正式管理端治理执行安全壳预览和正式管理端计划版本预览已开始吸收,qiming-backend 新增 `plan_governance` / `plan_revisions` 视图聚合治理动作、升级请求、人工介入和版本复核摘要,qiming“数字员工运营台”的“计划治理”“计划版本”tab 只记录标记已看、忽略、请求版本复核和记录升级等低风险意图;`hard_interrupt`、`activate_plan`、`cancel_run`、`cancel_task`、`accept_remote_approval_batch` 继续返回明确禁用 reason。正式管理端治理策略预览和计划版本物化预览已开始吸收,qiming-backend 新增 `governance_policies` / `plan_revision_materialization` 视图聚合治理策略、策略保护、版本事实来源、基线版本和变更字段,qiming“数字员工运营台”的“治理策略”“版本物化”tab 只记录策略拉取、复核、忽略和版本复核意图。策略治理审批路由总收口已开始吸收,治理策略详情补充高风险执行禁用和策略编辑/合并复核请求安全壳,不开放真实 ACP 高风险执行。 - 缺口:正式计划 revision 物理表、治理策略编辑 UI 和对真实 ACP 执行流的硬中断仍不完整;计划版本物化预览不自动激活计划、不取消运行或任务。 - 建议:下一轮围绕正式管理端治理策略和 ACP 硬中断安全设计,把本地治理审计推进到完整跨端裁决与执行控制。 @@ -588,7 +588,7 @@ GET /api/digital-employee/approvals - 建议:下一轮围绕正式管理端生命周期工作台和跨设备图谱,把 embedded 预览聚合推进到跨端运营治理。 8. **审批 / 人工介入增强** - - 已吸收:approval 记录、workday decision、ACP permission 响应桥已有雏形;用户处理审批时会写入 `approval_decision` runtime event;评论、转交、超时、风险标记会写入 `approval_action_recorded` / `approval_action_rejected`,并联动 embedded `/api/approval/:approvalId/timeline`、审批列表摘要、管理端 `/api/digital-employee/approval-history` 历史视图、`/api/approval/subscriptions` 订阅策略和 notification 投影;风险审批策略已支持管理端远端下发、手动拉取、本地策略评估、高风险动作前置拦截和本地设置页管理,`/api/risk-approval/policy` 可读写本地策略并记录 `update_risk_approval_policy`;多步审批 workflow 已复用 `digital_approvals.payload`,支持步骤推进、转交/超时/风险动作更新当前步骤、管理端审批更新拉取和审批处理回写,审批动作历史、风险策略、多步流程与通知联动已开始闭环;跨端审批冲突可视化已开始吸收,管理端远端更新与本地终态/更新时间/current step 不一致时会保留本地状态、记录 `approval_conflict_detected`、在业务视图投影 `approval_conflict_*` 字段,并在首页审批卡暂停直接同意/驳回;冲突解决动作闭环已开始吸收,客户端可通过独立 `resolveApprovalConflict` bridge 选择保留本地、采纳管理端或标记已复核,裁决会写入 `approval_conflict_resolved` / `approval_conflict_resolution_rejected`,并在审批业务视图投影 `approval_conflict_resolution`、`approval_conflict_resolved_at` 和裁决人摘要。跨设备批量冲突排查已开始吸收,embedded 新增“介入台”聚合审批冲突、风险审批、路由干预和待处理通知,支持筛选、分页、批量保留本地和批量标记复核;批量采纳管理端被 `batch_accept_remote_disabled` 保护,避免扩大状态分叉。正式管理端审批工作台预览已开始吸收,qiming-backend 新增 `approval_governance` 运营台视图聚合审批冲突、风险审批、路由待审和审批动作历史,qiming“数字员工运营台”新增“审批治理” tab,并只开放保留本地、标记复核、标记已看、忽略和重试策略检查等低风险动作意图。正式管理端审批裁决策略预览已开始吸收,审批治理详情可展示 `approval_policy_id`、`conflict_policy_id`、`decision_guard_status`、`batch_accept_remote_disabled`、`auto_decision_disabled` 和策略原因,用于解释为什么只允许低风险复核或保留本地。正式管理端审批策略预览已开始吸收,qiming“数字员工运营台”的“审批策略”tab 可独立筛选审批策略、冲突策略、风险审批策略和裁决保护快照,只记录批量拉取策略、标记已看和忽略意图,不远程编辑策略、不自动裁决、不批量采纳远端。远程策略编辑安全壳预览已开始吸收,qiming“数字员工运营台”的“策略中心”会显示远程策略编辑请求、安全禁用和自动合并禁用状态,只记录远程策略编辑请求、标记已看和忽略意图,不开放真实远程编辑或自动合并。 + - 已吸收:approval 记录、workday decision、ACP permission 响应桥已有雏形;用户处理审批时会写入 `approval_decision` runtime event;评论、转交、超时、风险标记会写入 `approval_action_recorded` / `approval_action_rejected`,并联动 embedded `/api/approval/:approvalId/timeline`、审批列表摘要、管理端 `/api/digital-employee/approval-history` 历史视图、`/api/approval/subscriptions` 订阅策略和 notification 投影;风险审批策略已支持管理端远端下发、手动拉取、本地策略评估、高风险动作前置拦截和本地设置页管理,`/api/risk-approval/policy` 可读写本地策略并记录 `update_risk_approval_policy`;多步审批 workflow 已复用 `digital_approvals.payload`,支持步骤推进、转交/超时/风险动作更新当前步骤、管理端审批更新拉取和审批处理回写,审批动作历史、风险策略、多步流程与通知联动已开始闭环;跨端审批冲突可视化已开始吸收,管理端远端更新与本地终态/更新时间/current step 不一致时会保留本地状态、记录 `approval_conflict_detected`、在业务视图投影 `approval_conflict_*` 字段,并在首页审批卡暂停直接同意/驳回;冲突解决动作闭环已开始吸收,客户端可通过独立 `resolveApprovalConflict` bridge 选择保留本地、采纳管理端或标记已复核,裁决会写入 `approval_conflict_resolved` / `approval_conflict_resolution_rejected`,并在审批业务视图投影 `approval_conflict_resolution`、`approval_conflict_resolved_at` 和裁决人摘要。跨设备批量冲突排查已开始吸收,embedded 新增“介入台”聚合审批冲突、风险审批、路由干预和待处理通知,支持筛选、分页、批量保留本地和批量标记复核;批量采纳管理端被 `batch_accept_remote_disabled` 保护,避免扩大状态分叉。正式管理端审批工作台预览已开始吸收,qiming-backend 新增 `approval_governance` 运营台视图聚合审批冲突、风险审批、路由待审和审批动作历史,qiming“数字员工运营台”新增“审批治理” tab,并只开放保留本地、标记复核、标记已看、忽略和重试策略检查等低风险动作意图。正式管理端审批裁决策略预览已开始吸收,审批治理详情可展示 `approval_policy_id`、`conflict_policy_id`、`decision_guard_status`、`batch_accept_remote_disabled`、`auto_decision_disabled` 和策略原因,用于解释为什么只允许低风险复核或保留本地。正式管理端审批策略预览已开始吸收,qiming“数字员工运营台”的“审批策略”tab 可独立筛选审批策略、冲突策略、风险审批策略和裁决保护快照,只记录批量拉取策略、标记已看和忽略意图,不远程编辑策略、不自动裁决、不批量采纳远端。远程策略编辑安全壳预览已开始吸收,qiming“数字员工运营台”的“策略中心”会显示远程策略编辑请求、安全禁用和自动合并禁用状态,只记录远程策略编辑请求、标记已看和忽略意图,不开放真实远程编辑或自动合并。策略治理审批路由总收口已开始吸收,审批策略详情新增审批策略编辑禁用、自动裁决禁用和批量采纳远端禁用解释,审批裁决仍只通过低风险请求和复核入口推进。 - 缺口:仍缺少管理端可编辑裁决策略 UI、批量采纳远端和高风险审批自动执行。 - 建议:下一轮围绕正式管理端审批工作台,把 embedded 介入台预览推进到外部管理端批量排查和策略化回写。 @@ -599,7 +599,7 @@ GET /api/digital-employee/approvals 10. **入口路由 / 任务分流(路由业务视图与通知联动已开始)** - 已吸收:管理端通过 `/computer/chat` 进入 qimingclaw 后会生成本地任务事实;embedded `/api/ingress/route`、`/api/route-decisions` 和 `/api/digital-employee/route-decisions` 已由 qimingclaw adapter 接管。 - - 当前能力:入口请求会按上下文、ready PlanStep、调度、查询和控制动作生成 route decision,并写入 `digital_events.kind = route_decision` 与现有 event outbox;路由时间线优先从 qimingclaw 本地事件读取;低风险 route decision 已开始映射为明确的本地 governance command,并把 `created_command_id` 回填到 route decision;自动映射前会执行低风险/受保护高风险 action guard,未允许的控制动作会记录 `route_action_policy_blocked` 或 `route_action_missing_context`,避免静默创建命令;入口路由策略可从管理端远端下发,支持按 intent 阻断、按 intent 要求审批、限制自动创建 governance command,并在策略干预时先记录 route decision 再停止本地命令执行;首页已展示路由干预队列和治理审计摘要,审批通过/拒绝会写入本地 approval decision 并回写管理端,管理端审批更新拉取后会自动扫描已批准 route approval 并按幂等规则恢复执行;scheduler check 已接入 ready PlanStep 安全派发,首页立即执行后会触发一次派发 sweep 并展示派发结果;路由决策本地业务视图、首页摘要、治理审计、策略来源/拉取错误、阻断/待审/待干预/已恢复计数和通知联动已开始闭环。管理端正式路由 UI 预览已开始吸收,“策略中心”会把入口路由策略纳入统一拉取与状态展示。正式管理端策略中心预览已开始吸收,qiming 运营台可从 `policy_center` 查看入口路由策略快照和拉取状态。正式管理端路由治理预览已开始吸收,qiming-backend 的 `route_governance` 运营台视图会聚合 route blocked / approval required / missing context / not mapped / governance audit,qiming“数字员工运营台”的“路由治理”tab 只开放标记已看、忽略和重试策略检查等低风险动作意图。跨设备路由干预冲突可视化已开始吸收,qiming-backend 的策略快照读模型会把 route blocked / approval required / missing context / not mapped 汇总为 `intervention_conflict_status` 和干预计数,qiming“策略冲突”tab 可展示跨设备路由干预冲突,但不恢复执行、不绕过审批、不远程改策略。 + - 当前能力:入口请求会按上下文、ready PlanStep、调度、查询和控制动作生成 route decision,并写入 `digital_events.kind = route_decision` 与现有 event outbox;路由时间线优先从 qimingclaw 本地事件读取;低风险 route decision 已开始映射为明确的本地 governance command,并把 `created_command_id` 回填到 route decision;自动映射前会执行低风险/受保护高风险 action guard,未允许的控制动作会记录 `route_action_policy_blocked` 或 `route_action_missing_context`,避免静默创建命令;入口路由策略可从管理端远端下发,支持按 intent 阻断、按 intent 要求审批、限制自动创建 governance command,并在策略干预时先记录 route decision 再停止本地命令执行;首页已展示路由干预队列和治理审计摘要,审批通过/拒绝会写入本地 approval decision 并回写管理端,管理端审批更新拉取后会自动扫描已批准 route approval 并按幂等规则恢复执行;scheduler check 已接入 ready PlanStep 安全派发,首页立即执行后会触发一次派发 sweep 并展示派发结果;路由决策本地业务视图、首页摘要、治理审计、策略来源/拉取错误、阻断/待审/待干预/已恢复计数和通知联动已开始闭环。管理端正式路由 UI 预览已开始吸收,“策略中心”会把入口路由策略纳入统一拉取与状态展示。正式管理端策略中心预览已开始吸收,qiming 运营台可从 `policy_center` 查看入口路由策略快照和拉取状态。正式管理端路由治理预览已开始吸收,qiming-backend 的 `route_governance` 运营台视图会聚合 route blocked / approval required / missing context / not mapped / governance audit,qiming“数字员工运营台”的“路由治理”tab 只开放标记已看、忽略和重试策略检查等低风险动作意图。跨设备路由干预冲突可视化已开始吸收,qiming-backend 的策略快照读模型会把 route blocked / approval required / missing context / not mapped 汇总为 `intervention_conflict_status` 和干预计数,qiming“策略冲突”tab 可展示跨设备路由干预冲突,但不恢复执行、不绕过审批、不远程改策略。策略治理审批路由总收口已开始吸收,路由治理详情展示路由策略编辑禁用、恢复安全壳和高风险恢复执行不开放,恢复执行只保留复查与请求入口。 - 后续缺口:路由策略编辑和高风险恢复执行策略 UI 仍待吸收。 - 建议:下一轮围绕管理端正式路由 UI 和跨设备冲突可视化,把 embedded 批量干预入口推进到管理端运营台。 @@ -609,7 +609,7 @@ GET /api/digital-employee/approvals - 建议:下一轮围绕管理端正式审计视图和诊断修复动作,把本地可追溯审计继续推进到可运营处置。 12. **管理端业务查询模型** - - 已吸收:管理端已有通用 sync record 接收、查询、payload 摘要和深链;后端 sync record 已开始保存客户端顶层 `contract_version`、`entity_summary` 和 `business_view`,并基于现有 `digital_employee_sync_record` 拆出 `/api/digital-employee/plans`、`/plans/{plan_id}`、`/tasks`、`/runs`、`/events`、`/artifacts`、`/approvals` 管理端业务查询模型;列表支持客户端 Key、设备、实体 ID、状态、同步状态和同步时间范围过滤,业务 row 会提炼 title/status/summary、Plan/Task/Run 关联、发生/开始/结束时间、sync record 深链和脱敏客户端 Key;Plan 视图会聚合 task/run 计数与最近事件,Plan 详情会返回关联 tasks/runs/events/artifacts/approvals 摘要。embedded adapter 已拆出同名只读业务查询接口,从 qimingclaw runtime、artifact、approval 和 canonical event 投影生成统一分页视图;embedded 数字员工页已新增“业务视图”入口,支持计划/任务/运行/事件/产物/审批分段、实体/状态/同步状态/时间筛选、分页和详情 JSON 查看;客户端 outbox 的 event `business_view` 已开始按 route decision、governance command、approval action、artifact lifecycle/access、skill call audit 和 ready PlanStep dispatch 细分常用字段,本地与管理端业务视图投影已开始闭环。正式外部管理端台面已开始吸收:qiming-backend 新增 `/api/digital-employee/admin/workbench/{view}`,qiming 管理端新增“数字员工运营台”,统一展示业务视图、介入台、产物治理、通知运营、审计诊断、日报运营和路由治理,只读消费 sync record `business_view` 并提供同步记录深链。管理端低风险动作闭环已开始吸收:后端新增 `/api/digital-employee/admin/actions` 记录动作意图和 `/pending` 拉取队列,qimingclaw 只应用保留本地、标记复核、通知已读/忽略、产物保留/到期、日报交付/确认/审批请求等低风险动作并写入 `admin_action_applied` / `admin_action_rejected` / `admin_action_failed`,qiming 运营台抽屉只提示“已记录,等待客户端拉取”。跨设备策略冲突视图已开始吸收:qimingclaw 将派发、风险审批和入口路由策略同步事件投影为 `policy_snapshot` business view,后端新增 `/api/digital-employee/admin/policies/snapshots` 聚合 client/device 维度差异并输出 `conflict_keys`,qiming 运营台新增“策略冲突” tab 展示 policy drift 与 sync record 深链。跨设备审批策略冲突预览已开始吸收:策略冲突页会把 `approval_policy_id`、`conflict_policy_id`、`decision_guard_status`、`batch_accept_remote_disabled`、`auto_decision_disabled` 和策略原因纳入冲突字段和详情解释,但不自动合并策略、不远程编辑策略。跨设备调度派发策略冲突预览已开始吸收:策略冲突页会把 `dispatch_policy_key`、`max_per_sweep`、`auto_dispatch_ready_steps`、`catch_up_on_startup` 和 `max_run_history` 纳入冲突字段和详情解释,但不立即运行、不接管租约、不远程改策略。正式管理端策略中心预览和正式管理端调度运营预览已开始吸收:`admin/workbench` 新增 `policy_center` 与 `scheduler_operations` 视图,统一消费策略快照、scheduler job、cron settings、lease、dispatch sweep 和 recent run 摘要,并通过低风险 admin action 记录策略拉取、立即检查调度、复核和忽略意图。正式管理端治理策略预览和计划版本物化预览已开始吸收:`admin/workbench` 新增 `governance_policies` 与 `plan_revision_materialization`,从 sync record / business fact 投影治理策略保护、ACP 硬中断禁用、版本事实来源和变更字段。业务事实物化查询已开始吸收:后端新增 `digital_employee_business_fact`、`digital_employee_artifact_lifecycle`、`digital_employee_daily_report_fact` 三张增量物化表,同步入库时 upsert 安全业务摘要,业务查询优先返回 `materialized: true` 行,旧记录继续回退 sync record。 + - 已吸收:管理端已有通用 sync record 接收、查询、payload 摘要和深链;后端 sync record 已开始保存客户端顶层 `contract_version`、`entity_summary` 和 `business_view`,并基于现有 `digital_employee_sync_record` 拆出 `/api/digital-employee/plans`、`/plans/{plan_id}`、`/tasks`、`/runs`、`/events`、`/artifacts`、`/approvals` 管理端业务查询模型;列表支持客户端 Key、设备、实体 ID、状态、同步状态和同步时间范围过滤,业务 row 会提炼 title/status/summary、Plan/Task/Run 关联、发生/开始/结束时间、sync record 深链和脱敏客户端 Key;Plan 视图会聚合 task/run 计数与最近事件,Plan 详情会返回关联 tasks/runs/events/artifacts/approvals 摘要。embedded adapter 已拆出同名只读业务查询接口,从 qimingclaw runtime、artifact、approval 和 canonical event 投影生成统一分页视图;embedded 数字员工页已新增“业务视图”入口,支持计划/任务/运行/事件/产物/审批分段、实体/状态/同步状态/时间筛选、分页和详情 JSON 查看;客户端 outbox 的 event `business_view` 已开始按 route decision、governance command、approval action、artifact lifecycle/access、skill call audit 和 ready PlanStep dispatch 细分常用字段,本地与管理端业务视图投影已开始闭环。正式外部管理端台面已开始吸收:qiming-backend 新增 `/api/digital-employee/admin/workbench/{view}`,qiming 管理端新增“数字员工运营台”,统一展示业务视图、介入台、产物治理、通知运营、审计诊断、日报运营和路由治理,只读消费 sync record `business_view` 并提供同步记录深链。管理端低风险动作闭环已开始吸收:后端新增 `/api/digital-employee/admin/actions` 记录动作意图和 `/pending` 拉取队列,qimingclaw 只应用保留本地、标记复核、通知已读/忽略、产物保留/到期、日报交付/确认/审批请求等低风险动作并写入 `admin_action_applied` / `admin_action_rejected` / `admin_action_failed`,qiming 运营台抽屉只提示“已记录,等待客户端拉取”。跨设备策略冲突视图已开始吸收:qimingclaw 将派发、风险审批和入口路由策略同步事件投影为 `policy_snapshot` business view,后端新增 `/api/digital-employee/admin/policies/snapshots` 聚合 client/device 维度差异并输出 `conflict_keys`,qiming 运营台新增“策略冲突” tab 展示 policy drift 与 sync record 深链。跨设备审批策略冲突预览已开始吸收:策略冲突页会把 `approval_policy_id`、`conflict_policy_id`、`decision_guard_status`、`batch_accept_remote_disabled`、`auto_decision_disabled` 和策略原因纳入冲突字段和详情解释,但不自动合并策略、不远程编辑策略。跨设备调度派发策略冲突预览已开始吸收:策略冲突页会把 `dispatch_policy_key`、`max_per_sweep`、`auto_dispatch_ready_steps`、`catch_up_on_startup` 和 `max_run_history` 纳入冲突字段和详情解释,但不立即运行、不接管租约、不远程改策略。正式管理端策略中心预览和正式管理端调度运营预览已开始吸收:`admin/workbench` 新增 `policy_center` 与 `scheduler_operations` 视图,统一消费策略快照、scheduler job、cron settings、lease、dispatch sweep 和 recent run 摘要,并通过低风险 admin action 记录策略拉取、立即检查调度、复核和忽略意图。正式管理端治理策略预览和计划版本物化预览已开始吸收:`admin/workbench` 新增 `governance_policies` 与 `plan_revision_materialization`,从 sync record / business fact 投影治理策略保护、ACP 硬中断禁用、版本事实来源和变更字段。策略治理审批路由总收口已开始吸收,`policy_center`、`governance_policies`、`approval_policies`、`route_governance` 会统一暴露策略编辑、合并、裁决和恢复执行安全壳字段,并把真实远程强编辑、自动合并、批量采纳远端、自动裁决和高风险恢复执行保持禁用。业务事实物化查询已开始吸收:后端新增 `digital_employee_business_fact`、`digital_employee_artifact_lifecycle`、`digital_employee_daily_report_fact` 三张增量物化表,同步入库时 upsert 安全业务摘要,业务查询优先返回 `materialized: true` 行,旧记录继续回退 sync record。 - 缺口:复杂组合检索、跨设备聚合图、远程裁决/策略编辑动作和物化业务表仍待吸收。 - 建议:先使用 sync record 派生业务查询模型和 embedded 业务视图预览承接联调,再按容量和查询压力决定是否拆 Plan / Task / Run / Event 物理业务表。