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 022af334..f277ad83 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 @@ -102,6 +102,16 @@ public class DigitalEmployeeAdminWorkbenchRowDto { private String memoryKey; @JsonProperty("skill_id") private String skillId; + @JsonProperty("skill_version") + private String skillVersion; + @JsonProperty("install_status") + private String installStatus; + @JsonProperty("permission_scope") + private String permissionScope; + @JsonProperty("policy_source") + private String policySource; + @JsonProperty("policy_status") + private String policyStatus; @JsonProperty("tool_name") private String toolName; @JsonProperty("server_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 8bb00195..82e8e93f 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 @@ -1093,6 +1093,11 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye .memoryId(firstString(businessView.get("memory_id"), businessView.get("memoryId"), payload.get("memory_id"), payload.get("memoryId"), "memory".equals(record.getEntityType()) ? record.getEntityId() : null)) .memoryKey(firstString(businessView.get("memory_key"), businessView.get("memoryKey"), businessView.get("key"), payload.get("memory_key"), payload.get("memoryKey"), payload.get("key"))) .skillId(firstString(businessView.get("skill_id"), businessView.get("skillId"), payload.get("skill_id"), payload.get("skillId"))) + .skillVersion(firstString(businessView.get("skill_version"), businessView.get("skillVersion"), businessView.get("version"), payload.get("skill_version"), payload.get("skillVersion"), payload.get("version"))) + .installStatus(firstString(businessView.get("install_status"), businessView.get("installStatus"), payload.get("install_status"), payload.get("installStatus"))) + .permissionScope(firstString(businessView.get("permission_scope"), businessView.get("permissionScope"), payload.get("permission_scope"), payload.get("permissionScope"))) + .policySource(firstString(businessView.get("policy_source"), businessView.get("policySource"), businessView.get("source"), payload.get("policy_source"), payload.get("policySource"), payload.get("source"))) + .policyStatus(firstString(businessView.get("policy_status"), businessView.get("policyStatus"), payload.get("policy_status"), payload.get("policyStatus"))) .toolName(firstString(businessView.get("tool_name"), businessView.get("toolName"), payload.get("tool_name"), payload.get("toolName"))) .serverId(firstString(businessView.get("server_id"), businessView.get("serverId"), payload.get("server_id"), payload.get("serverId"))) .policyKind(firstString(businessView.get("policy_kind"), businessView.get("policyKind"), payload.get("policy_kind"), payload.get("policyKind"))) @@ -1236,11 +1241,17 @@ public class DigitalEmployeeSyncApplicationServiceImpl implements DigitalEmploye String kind = firstString(row.getKind()); return StringUtils.equals(category, "skill") || StringUtils.equals(category, "skill_call_audit") + || StringUtils.equals(category, "skill_catalog") + || StringUtils.equals(category, "skill_policy") || startsWithAny(kind, "skill_call_") + || startsWithAny(kind, "skill_catalog", "skill_policy") || StringUtils.equals(category, "tool_call_audit") || StringUtils.equals(category, "sandbox_audit") || StringUtils.equals(category, "token_cost") || StringUtils.isNotBlank(row.getSkillId()) + || StringUtils.isNotBlank(row.getSkillVersion()) + || StringUtils.isNotBlank(row.getInstallStatus()) + || StringUtils.isNotBlank(row.getPermissionScope()) || StringUtils.isNotBlank(row.getToolName()) || StringUtils.isNotBlank(row.getServerId()); } 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 6102af74..397ad7ba 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 @@ -461,6 +461,67 @@ class DigitalEmployeeSyncApplicationServiceImplTest { assertThat(result.getRecords().get(2).getBusinessView()).containsEntry("total_tokens", 180); } + @Test + void queryAdminWorkbenchSkillAuditsReturnsVersionPermissionAndPolicyRowsOnly() { + when(repository.queryBusinessRecords(eq(101L), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(1L), eq(20L))) + .thenReturn(page(List.of( + record("outbox-skill-catalog", "event", "event-skill-catalog-1", 101L, new Date(10_000), + Map.of( + "entity_type", "event", + "category", "skill_catalog", + "kind", "skill_catalog_snapshot", + "status", "enabled", + "title", "Browser skill installed", + "summary", "浏览器技能已安装", + "skill_id", "skill-web", + "skill_version", "1.2.3", + "install_status", "installed", + "permission_scope", "browser.open,browser.screenshot", + "policy_source", "local" + ), + Map.of("id", "event-skill-catalog-1", "kind", "skill_catalog_snapshot")), + record("outbox-skill-policy", "event", "event-skill-policy-1", 101L, new Date(11_000), + Map.of( + "entity_type", "event", + "category", "skill_policy", + "kind", "skill_policy_snapshot", + "status", "remote_disabled", + "title", "Browser skill policy", + "skill_id", "skill-web", + "skill_version", "1.2.3", + "policy_source", "remote", + "policy_status", "disabled", + "permission_scope", "browser.open" + ), + Map.of("id", "event-skill-policy-1", "kind", "skill_policy_snapshot")), + record("outbox-notification", "notification", "notification-1", 101L, new Date(12_000), + Map.of("entity_type", "notification", "title", "普通通知", "status", "unread"), + Map.of("id", "notification-1", "status", "unread")), + record("outbox-approval", "approval", "approval-1", 101L, new Date(13_000), + Map.of("entity_type", "approval", "title", "审批", "status", "pending"), + Map.of("id", "approval-1", "status", "pending")), + record("outbox-route", "event", "event-route-1", 101L, new Date(14_000), + Map.of("entity_type", "event", "kind", "route_decision_blocked", "title", "路由阻断", "status", "blocked"), + Map.of("id", "event-route-1", "kind", "route_decision_blocked")) + ))); + + DigitalEmployeeAdminWorkbenchPageDto result = service.queryAdminWorkbench( + "skill_audits", null, null, null, null, null, null, null, null, null, null, null, 1L, 20L + ); + + assertThat(result.getTotal()).isEqualTo(2); + assertThat(result.getRecords()).extracting(DigitalEmployeeAdminWorkbenchRowDto::getEntityId) + .containsExactly("event-skill-catalog-1", "event-skill-policy-1"); + assertThat(result.getRecords().get(0).getView()).isEqualTo("skill_audits"); + assertThat(result.getRecords().get(0).getSkillId()).isEqualTo("skill-web"); + assertThat(result.getRecords().get(0).getSkillVersion()).isEqualTo("1.2.3"); + assertThat(result.getRecords().get(0).getInstallStatus()).isEqualTo("installed"); + assertThat(result.getRecords().get(0).getPermissionScope()).isEqualTo("browser.open,browser.screenshot"); + assertThat(result.getRecords().get(0).getPolicySource()).isEqualTo("local"); + assertThat(result.getRecords().get(1).getPolicySource()).isEqualTo("remote"); + assertThat(result.getRecords().get(1).getPolicyStatus()).isEqualTo("disabled"); + } + @Test void queryAdminWorkbenchPolicyCenterReturnsPolicySnapshotRowsOnly() { when(repository.queryBusinessRecords(eq(101L), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(1L), eq(20L))) diff --git a/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx b/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx index d2db5e4e..b5445fc8 100644 --- a/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx +++ b/qiming/src/pages/SystemManagement/Content/DigitalEmployeeOps/index.tsx @@ -128,6 +128,10 @@ function renderSummary(record: DigitalEmployeeAdminWorkbenchRow) { record.daily_report_fact_id && `ReportFact ${record.daily_report_fact_id}`, record.memory_id && `Memory ${record.memory_id}`, record.skill_id && `Skill ${record.skill_id}`, + record.skill_version && `Version ${record.skill_version}`, + record.install_status && `Install ${record.install_status}`, + record.permission_scope && `Scope ${record.permission_scope}`, + record.policy_source && `PolicySource ${record.policy_source}`, record.tool_name && `Tool ${record.tool_name}`, record.schedule_id && `Schedule ${record.schedule_id}`, record.job_id && `Job ${record.job_id}`, @@ -835,6 +839,8 @@ const DigitalEmployeeOps: React.FC = () => { detailRecord.memory_id && `Memory ${detailRecord.memory_id}`, detailRecord.memory_key && `MemoryKey ${detailRecord.memory_key}`, detailRecord.skill_id && `Skill ${detailRecord.skill_id}`, + detailRecord.skill_version && `Version ${detailRecord.skill_version}`, + detailRecord.policy_source && `PolicySource ${detailRecord.policy_source}`, detailRecord.tool_name && `Tool ${detailRecord.tool_name}`, detailRecord.server_id && `Server ${detailRecord.server_id}`, detailRecord.schedule_id && `Schedule ${detailRecord.schedule_id}`, @@ -977,9 +983,14 @@ const DigitalEmployeeOps: React.FC = () => { 技能审计: {detailRecord.skill_id || detailRecord.tool_name || detailRecord.category || '--'} {detailRecord.status && ` · ${detailRecord.status}`} + {detailRecord.skill_version && ` · 版本 ${detailRecord.skill_version}`} + {detailRecord.install_status && ` · 安装状态 ${detailRecord.install_status}`} + {detailRecord.permission_scope && ` · 权限范围 ${detailRecord.permission_scope}`} + {detailRecord.policy_source && ` · 策略来源 ${detailRecord.policy_source}`} + {detailRecord.policy_status && ` · 策略状态 ${detailRecord.policy_status}`} {' '} - · 只消费调用审计和安全摘要,不安装技能、不变更权限范围。 + · 展示版本、安装状态、权限范围和策略来源,只消费调用审计和安全摘要,不安装技能、不变更权限范围。 )} diff --git a/qiming/src/types/interfaces/systemManage.ts b/qiming/src/types/interfaces/systemManage.ts index 8d44e281..9a1eca6f 100644 --- a/qiming/src/types/interfaces/systemManage.ts +++ b/qiming/src/types/interfaces/systemManage.ts @@ -628,6 +628,11 @@ export interface DigitalEmployeeAdminWorkbenchRow { memory_id?: string; memory_key?: string; skill_id?: string; + skill_version?: string; + install_status?: string; + permission_scope?: string; + policy_source?: string; + policy_status?: string; tool_name?: string; server_id?: string; policy_kind?: string; 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 aa8cbf1e..57ffbbab 100644 --- a/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js +++ b/qimingclaw/crates/agent-electron-client/scripts/check-digital-employee.js @@ -1585,19 +1585,29 @@ function checkDigitalAdminMemorySkillWorkbench() { [dto, "memory_id", "backend memory id row field"], [dto, "memory_key", "backend memory key row field"], [dto, "skill_id", "backend skill id row field"], + [dto, "skill_version", "backend skill version row field"], + [dto, "install_status", "backend skill install status row field"], + [dto, "permission_scope", "backend skill permission scope row field"], + [dto, "policy_source", "backend skill policy source row field"], [dto, "tool_name", "backend tool name row field"], [dto, "server_id", "backend server id row field"], [test, "queryAdminWorkbenchMemoryGovernanceReturnsMemoryRowsOnly", "backend memory governance test"], [test, "queryAdminWorkbenchSkillAuditsReturnsSkillAndToolAuditRowsOnly", "backend skill audits test"], + [test, "queryAdminWorkbenchSkillAuditsReturnsVersionPermissionAndPolicyRowsOnly", "backend skill version permission test"], [qimingTypes, "memory_governance", "qiming memory governance view type"], [qimingTypes, "skill_audits", "qiming skill audits view type"], + [qimingTypes, "skill_version", "qiming skill version field"], + [qimingTypes, "permission_scope", "qiming skill permission scope field"], [qimingOps, "记忆治理", "qiming memory governance tab"], [qimingOps, "技能审计", "qiming skill audits tab"], + [qimingOps, "权限范围", "qiming skill permission scope copy"], + [qimingOps, "策略来源", "qiming skill policy source copy"], [qimingOps, "批量归档", "qiming memory archive action"], [qimingOps, "批量恢复", "qiming memory restore action"], [qimingOps, "重复候选", "qiming memory duplicate copy"], [docs, "正式管理端记忆治理预览", "docs memory governance absorption"], [docs, "正式管理端技能审计预览", "docs skill audit absorption"], + [docs, "正式管理端技能版本权限预览", "docs skill version permission absorption"], ]; const missing = requiredSnippets .filter(([content, snippet]) => !content.includes(snippet)) diff --git a/qimingclaw/docs/digital-employee-frontend-integration-plan.md b/qimingclaw/docs/digital-employee-frontend-integration-plan.md index 0c10bd3f..2c27ae47 100644 --- a/qimingclaw/docs/digital-employee-frontend-integration-plan.md +++ b/qimingclaw/docs/digital-employee-frontend-integration-plan.md @@ -578,8 +578,8 @@ GET /api/digital-employee/approvals - 建议:先沉淀安全重启审计和人工确认体验,再评估更细粒度的服务恢复策略。 6. **技能生命周期与策略** - - 已吸收:ACP / MCP / GUI / file server 能力已投射到 Skill Catalog,技能库不再使用 demo fallback;Skill Library 启用/禁用已写入 qimingclaw 本地 `digital_employee_skill_policies_v1.local_skills`,管理端下发策略写入 `remote_skills`,远端禁用优先生效且不会被本地按钮覆盖;入口路由候选技能会按有效策略过滤;MCP 注入 Agent 前会应用有效技能策略,ACP permission / tool update 会写入 `skill_call_allowed` / `skill_call_rejected` / `skill_call_observed` 审计事件,`policy_snapshot` 会包含本地/远端策略命中字段;embedded 已提供 `/api/digital-employee/skill-call-audits` 只读投影、`POST /api/skill/policies/pull` 手动拉取入口,技能调用策略与审计已开始闭环。管理端调用审计视图已开始吸收,embedded 新增“技能审计”入口,将 skill call audit、tool_call_audit、sandbox_audit 和 token_cost 统一成可筛选列表,技能库可按 skill id 跳转聚焦。正式管理端技能审计预览已开始吸收,qiming-backend 新增 `skill_audits` 运营台视图聚合 skill call、tool、sandbox 和 token/cost 安全摘要,qiming“数字员工运营台”的“技能审计”tab 只开放标记复核和忽略意图,不安装技能、不变更权限范围。 - - 缺口:仍缺少技能安装、版本管理和细粒度权限范围。 + - 已吸收:ACP / MCP / GUI / file server 能力已投射到 Skill Catalog,技能库不再使用 demo fallback;Skill Library 启用/禁用已写入 qimingclaw 本地 `digital_employee_skill_policies_v1.local_skills`,管理端下发策略写入 `remote_skills`,远端禁用优先生效且不会被本地按钮覆盖;入口路由候选技能会按有效策略过滤;MCP 注入 Agent 前会应用有效技能策略,ACP permission / tool update 会写入 `skill_call_allowed` / `skill_call_rejected` / `skill_call_observed` 审计事件,`policy_snapshot` 会包含本地/远端策略命中字段;embedded 已提供 `/api/digital-employee/skill-call-audits` 只读投影、`POST /api/skill/policies/pull` 手动拉取入口,技能调用策略与审计已开始闭环。管理端调用审计视图已开始吸收,embedded 新增“技能审计”入口,将 skill call audit、tool_call_audit、sandbox_audit 和 token_cost 统一成可筛选列表,技能库可按 skill id 跳转聚焦。正式管理端技能审计预览已开始吸收,qiming-backend 新增 `skill_audits` 运营台视图聚合 skill call、tool、sandbox 和 token/cost 安全摘要,qiming“数字员工运营台”的“技能审计”tab 只开放标记复核和忽略意图,不安装技能、不变更权限范围。正式管理端技能版本权限预览已开始吸收,`skill_audits` 继续聚合 skill catalog / skill policy snapshot,qiming 详情可展示技能版本、安装状态、权限范围、策略来源和策略状态,用于解释权限命中但不开放安装或权限编辑。 + - 缺口:仍缺少真实技能安装、版本升级管理和细粒度权限范围编辑。 - 建议:下一轮围绕管理端审计消费、技能安装和权限范围,把本地调用审计推进到更完整的跨端治理闭环。 7. **产物交付闭环**