- ) : null}
+ {evidenceChecks.length > 0 ? (
+
+ ) : null}
+
{canVerify || canRegister ? (
{canRegister
? t(
"capabilityDraft.panel.action.registerHelp",
- "注册只写当前 Workspace 的 .agents/skills,不接运行或自动化。",
+ "保存后会出现在“已保存技能”里,你可以再选择试用或设置定时运行。",
)
: t(
"capabilityDraft.panel.action.verifyHelp",
- "只做静态门禁检查,不执行草案脚本。",
+ "先检查结构和权限,不会自动运行这条技能。",
)}
@@ -630,7 +653,7 @@ export function CapabilityDraftPanel({
isVerifying && "animate-spin",
)}
/>
- {t("capabilityDraft.panel.action.verify", "运行验证")}
+ {t("capabilityDraft.panel.action.verify", "检查")}
) : null}
{canRegister ? (
@@ -653,7 +676,7 @@ export function CapabilityDraftPanel({
/>
{t(
"capabilityDraft.panel.action.register",
- "注册到 Workspace",
+ "保存到项目",
)}
) : null}
diff --git a/src/features/capability-drafts/components/WorkspaceRegisteredSkillsPanel.test.tsx b/src/features/capability-drafts/components/WorkspaceRegisteredSkillsPanel.test.tsx
index b7785b7d1..60ebe4d79 100644
--- a/src/features/capability-drafts/components/WorkspaceRegisteredSkillsPanel.test.tsx
+++ b/src/features/capability-drafts/components/WorkspaceRegisteredSkillsPanel.test.tsx
@@ -138,7 +138,7 @@ describe("WorkspaceRegisteredSkillsPanel", () => {
it("没有项目根目录时只显示选择项目提示,不读取已注册能力", () => {
const { container } = renderPanel();
- expect(container.textContent).toContain("Workspace 已注册能力");
+ expect(container.textContent).toContain("已保存技能");
expect(container.textContent).toContain("选择或进入一个项目");
expect(capabilityDraftsApi.listRegisteredSkills).not.toHaveBeenCalled();
expect(listWorkspaceSkillBindings).not.toHaveBeenCalled();
@@ -459,7 +459,7 @@ describe("WorkspaceRegisteredSkillsPanel", () => {
});
expect(container.textContent).toContain("只读 CLI 报告");
expect(container.textContent).toContain("已注册");
- expect(container.textContent).toContain("P3C binding 候选");
+ expect(container.textContent).toContain("可试用");
expect(container.textContent).toContain("capdraft-1 / capver-1");
expect(container.textContent).toContain("注册 provenance");
expect(container.textContent).toContain("只读 HTTP 执行 preflight");
@@ -555,14 +555,12 @@ describe("WorkspaceRegisteredSkillsPanel", () => {
"当前仍未注入 Query Loop 或 tool_runtime",
);
expect(container.textContent).toContain("manual_runtime_enable");
- expect(container.textContent).toContain("Agent envelope 草案");
- expect(container.textContent).toContain("等待成功运行");
+ expect(container.textContent).toContain("项目助手");
+ expect(container.textContent).toContain("待试用");
expect(container.textContent).toContain(
- "成功运行后可把 Skill、权限、手动 rerun 和 evidence 组合成 Workspace Agent envelope。",
- );
- expect(container.textContent).toContain(
- "Evidence:还没有成功运行证据;先通过本回合启用拿到一次结果。",
+ "试用结果没问题后,可以把它保存成当前项目里的助手。",
);
+ expect(container.textContent).toContain("最近结果:还没有试用结果。");
expect(container.textContent).toContain("Managed Job:未创建");
expect(container.textContent).not.toContain("立即运行");
expect(container.textContent).not.toContain("创建自动化");
@@ -927,11 +925,13 @@ describe("WorkspaceRegisteredSkillsPanel", () => {
) as HTMLButtonElement | null;
expect(managedButton).toBeTruthy();
expect(managedButton?.disabled).toBe(false);
- expect(container.textContent).toContain("Agent card:等待 evidence-ready");
- expect(container.textContent).toContain("Sharing:未完成审计前");
- expect(container.textContent).toContain("Discovery:同 workspace 成员");
- expect(container.textContent).toContain("Memory:引用 verification report");
- expect(container.textContent).toContain("Widget:等待运行后展示状态");
+ expect(container.textContent).toContain("项目助手:试用通过后再保存。");
+ expect(container.textContent).toContain("共享:先只对你可见。");
+ expect(container.textContent).toContain(
+ "团队可见性:当前项目成员可以使用这条技能。",
+ );
+ expect(container.textContent).toContain("记录:已保留检查结果");
+ expect(container.textContent).toContain("结果:试用后展示最近状态");
expect(container.textContent).toContain("Managed Job:草案暂停");
expect(container.textContent).toContain("Schedule:Cron 0 9 * * *");
expect(container.textContent).toContain("Managed Objective:paused");
@@ -943,7 +943,7 @@ describe("WorkspaceRegisteredSkillsPanel", () => {
),
).toHaveLength(1);
expect(toggleButton).toBeTruthy();
- expect(toggleButton?.textContent).toContain("恢复 Managed Job");
+ expect(toggleButton?.textContent).toContain("开启定时运行");
expect(auditButton).toBeTruthy();
await act(async () => {
@@ -983,10 +983,12 @@ describe("WorkspaceRegisteredSkillsPanel", () => {
expect(exportAgentRuntimeEvidencePack).toHaveBeenCalledWith("session-1");
expect(container.textContent).toContain("completion audit completed");
expect(container.textContent).toContain(
- "Agent card:workspace-local/capability-report",
+ "项目助手:已准备好保存(capability-report)",
+ );
+ expect(container.textContent).toContain("共享:可在当前项目内共享");
+ expect(container.textContent).toContain(
+ "团队可见性:当前项目成员可以使用这条技能。",
);
- expect(container.textContent).toContain("workspace / team 内共享");
- expect(container.textContent).toContain("复用同一 Managed Job / evidence");
const envelopeButton = container.querySelector(
'[data-testid="workspace-registered-agent-envelope-action"]',
@@ -1274,10 +1276,10 @@ describe("WorkspaceRegisteredSkillsPanel", () => {
) as HTMLButtonElement | null;
expect(initialEnvelopeButton).toBeTruthy();
expect(initialEnvelopeButton?.disabled).toBe(true);
- expect(container.textContent).toContain("Agent card:等待 evidence-ready");
- expect(container.textContent).toContain("Sharing:未完成审计前");
+ expect(container.textContent).toContain("项目助手:试用通过后再保存。");
+ expect(container.textContent).toContain("共享:先只对你可见。");
expect(container.textContent).not.toContain(
- "Agent card:workspace-local/capability-report",
+ "项目助手:已准备好保存(capability-report)",
);
await act(async () => {
@@ -1300,11 +1302,12 @@ describe("WorkspaceRegisteredSkillsPanel", () => {
expect(completedEnvelopeButton?.disabled).toBe(false);
expect(container.textContent).toContain("completion audit completed");
expect(container.textContent).toContain(
- "Agent card:workspace-local/capability-report",
+ "项目助手:已准备好保存(capability-report)",
+ );
+ expect(container.textContent).toContain("共享:可在当前项目内共享");
+ expect(container.textContent).toContain(
+ "团队可见性:当前项目成员可以使用这条技能。",
);
- expect(container.textContent).toContain("workspace / team 内共享");
- expect(container.textContent).toContain("不进入 public Marketplace");
- expect(container.textContent).toContain("同 workspace 成员");
await act(async () => {
completedEnvelopeButton?.click();
@@ -1446,9 +1449,7 @@ describe("WorkspaceRegisteredSkillsPanel", () => {
await act(async () => {
await Promise.resolve();
});
- expect(container.textContent).toContain(
- "当前项目还没有通过 P3A 注册的能力",
- );
+ expect(container.textContent).toContain("当前项目还没有已保存技能");
await act(async () => {
root.render(
diff --git a/src/features/capability-drafts/components/WorkspaceRegisteredSkillsPanel.tsx b/src/features/capability-drafts/components/WorkspaceRegisteredSkillsPanel.tsx
index 89e55a87b..08de2a52f 100644
--- a/src/features/capability-drafts/components/WorkspaceRegisteredSkillsPanel.tsx
+++ b/src/features/capability-drafts/components/WorkspaceRegisteredSkillsPanel.tsx
@@ -215,6 +215,94 @@ function buildReadonlyHttpApprovalPreview(
};
}
+function summarizePermissionForUser(
+ skill: WorkspaceRegisteredSkillRecord,
+ copy: {
+ defaultReadonly: string;
+ readonly: string;
+ draftWrite: string;
+ localCommand: string;
+ },
+) {
+ if (skill.permissionSummary.length === 0) {
+ return copy.defaultReadonly;
+ }
+
+ const labels = new Set();
+ for (const item of skill.permissionSummary) {
+ const normalized = item.toLowerCase();
+ if (normalized.includes("level 0") || item.includes("只读")) {
+ labels.add(copy.readonly);
+ continue;
+ }
+ if (
+ normalized.includes("level 1") ||
+ normalized.includes("draft") ||
+ normalized.includes("write") ||
+ item.includes("草案") ||
+ item.includes("写入")
+ ) {
+ labels.add(copy.draftWrite);
+ continue;
+ }
+ if (
+ normalized.includes("cli") ||
+ normalized.includes("command") ||
+ item.includes("命令")
+ ) {
+ labels.add(copy.localCommand);
+ continue;
+ }
+ labels.add(item.replace(/^Level\s+\d+\s*/i, "").trim() || item);
+ }
+
+ return Array.from(labels).slice(0, 2).join(" / ");
+}
+
+function getUserScheduleLabel(
+ job: AutomationJobRecord | undefined,
+ copy: {
+ enabled: string;
+ notCreated: string;
+ paused: string;
+ },
+) {
+ if (!job) {
+ return copy.notCreated;
+ }
+ return job.enabled ? copy.enabled : copy.paused;
+}
+
+function getUserResultLabel(
+ job: AutomationJobRecord | undefined,
+ completionAuditSummary: AgentRuntimeCompletionAuditSummary | undefined,
+ copy: {
+ auditBlocked: string;
+ auditPassed: string;
+ noJob: string;
+ noRun: string;
+ readyToCheck: string;
+ running: string;
+ },
+) {
+ if (completionAuditSummary?.decision === "completed") {
+ return copy.auditPassed;
+ }
+ if (completionAuditSummary) {
+ return copy.auditBlocked;
+ }
+ if (!job) {
+ return copy.noJob;
+ }
+ if (job.last_status === "running") {
+ return copy.running;
+ }
+ if (job.last_status === "success") {
+ return copy.readyToCheck;
+ }
+ return copy.noRun;
+}
+
function sortRegisteredSkills(
skills: WorkspaceRegisteredSkillRecord[],
): WorkspaceRegisteredSkillRecord[] {
@@ -434,44 +522,43 @@ function WorkspaceRegisteredSkillCard({
() => ({
actionBlocked: t(
"capabilityDraft.registeredPanel.agentEnvelope.action.blocked",
- "先解除阻塞",
+ "先处理问题",
),
actionDraft: t(
"capabilityDraft.registeredPanel.agentEnvelope.action.draft",
- "转成 Agent 草案",
+ "保存成项目助手",
),
actionManualEnable: t(
"capabilityDraft.registeredPanel.agentEnvelope.action.manualEnable",
- "先本回合启用",
+ "先试用一次",
),
agentCardPending: t(
"capabilityDraft.registeredPanel.agentEnvelope.agentCard.pending",
- "Agent card:等待 evidence-ready 后派生,不创建平行持久化实体。",
+ "项目助手:试用通过后再保存。",
),
blockedReasonFallback: t(
"capabilityDraft.registeredPanel.agentEnvelope.blockedReasonFallback",
- "runtime binding 当前被 gate 阻断",
+ "当前还有问题需要处理",
),
description: t(
"capabilityDraft.registeredPanel.agentEnvelope.description",
- "成功运行后可把 Skill、权限、手动 rerun 和 evidence 组合成 Workspace Agent envelope。",
+ "试用结果没问题后,可以把它保存成当前项目里的助手。",
),
discoveryPending: t(
"capabilityDraft.registeredPanel.agentEnvelope.discovery.pending",
- "Discovery:等待 workspace-local skill 注册路径后再进入团队发现。",
+ "团队可见性:保存成项目助手后再开放。",
),
evidenceMissing: t(
"capabilityDraft.registeredPanel.agentEnvelope.evidence.missing",
- "Evidence:还没有成功运行证据;先通过本回合启用拿到一次结果。",
+ "最近结果:还没有试用结果。",
),
evidenceSourceMetadataOnly: t(
"capabilityDraft.registeredPanel.agentEnvelope.evidence.sourceMetadataOnly",
- "Evidence:已有 P3E source metadata,可追踪本次 session 授权来源。",
+ "最近结果:已记录这次试用来源,等待检查。",
),
formatAgentCardReady: (directory) =>
t("capabilityDraft.registeredPanel.agentEnvelope.agentCard.ready", {
- defaultValue:
- "Agent card:workspace-local/{{directory}},由已注册 Skill、Managed Job 和 completion audit 派生。",
+ defaultValue: "项目助手:已准备好保存({{directory}})。",
directory,
}),
formatCompletionAuditEvidenceLabel: buildCompletionAuditLabel,
@@ -480,32 +567,29 @@ function WorkspaceRegisteredSkillCard({
? t(
"capabilityDraft.registeredPanel.agentEnvelope.evidence.completedPack",
{
- defaultValue: "Evidence:已关联 evidence pack {{packId}}。",
+ defaultValue: "最近结果:已通过检查({{packId}})。",
packId,
},
)
: t(
"capabilityDraft.registeredPanel.agentEnvelope.evidence.completedPackFallback",
- "Evidence:已关联 evidence pack。",
+ "最近结果:已通过检查。",
),
formatDiscoveryReady: (registeredSkillDirectory) =>
t("capabilityDraft.registeredPanel.agentEnvelope.discovery.ready", {
- defaultValue:
- "Discovery:同 workspace 成员通过 registered skill 发现 {{directory}},复用同一 Managed Job / evidence。",
+ defaultValue: "团队可见性:当前项目成员可以使用这条技能。",
directory: registeredSkillDirectory,
}),
formatMemoryWithReport: (reportId) =>
t("capabilityDraft.registeredPanel.agentEnvelope.memory.withReport", {
- defaultValue:
- "Memory:引用 verification report {{reportId}} 与后续运行修正。",
+ defaultValue: "记录:已保留检查结果,后续会继续积累使用反馈。",
reportId,
}),
formatPendingEvidencePack: (packId) =>
t(
"capabilityDraft.registeredPanel.agentEnvelope.evidence.pendingPack",
{
- defaultValue:
- "Evidence:已关联 evidence pack {{packId}},但还缺 completed completion audit,不能固化为 Agent。",
+ defaultValue: "最近结果:已生成,等待检查后才能保存成项目助手。",
packId,
},
),
@@ -519,54 +603,54 @@ function WorkspaceRegisteredSkillCard({
),
formatRunbook: (name) =>
t("capabilityDraft.registeredPanel.agentEnvelope.runbook", {
- defaultValue: "Runbook:{{name}}",
+ defaultValue: "使用方式:{{name}}",
name,
}),
memoryPending: t(
"capabilityDraft.registeredPanel.agentEnvelope.memory.pending",
- "Memory:等待首轮运行后记录用户偏好、方法论和修正历史。",
+ "记录:等待首次试用后记录偏好和修正。",
),
permissionEmpty: t(
"capabilityDraft.registeredPanel.agentEnvelope.permission.empty",
- "权限:默认手动确认,未声明额外外部写权限。",
+ "权限:默认需要手动确认。",
),
schedule: t(
"capabilityDraft.registeredPanel.agentEnvelope.schedule",
- "Schedule:第一刀仅支持 manual rerun 草案,不创建长期任务。",
+ "定时运行:尚未设置。",
),
sharingPending: t(
"capabilityDraft.registeredPanel.agentEnvelope.sharing.pending",
- "Sharing:未完成审计前仅对当前操作者展示草案。",
+ "共享:先只对你可见。",
),
sharingReady: t(
"capabilityDraft.registeredPanel.agentEnvelope.sharing.ready",
- "Sharing:可在当前 workspace / team 内共享;不进入 public Marketplace。",
+ "共享:可在当前项目内共享。",
),
statusLabels: {
blocked: t(
"capabilityDraft.registeredPanel.agentEnvelope.status.blocked",
- "Agent 草案阻塞",
+ "需要处理",
),
evidence_ready: t(
"capabilityDraft.registeredPanel.agentEnvelope.status.evidenceReady",
- "Evidence 已就绪",
+ "可保存",
),
manual_enable_required: t(
"capabilityDraft.registeredPanel.agentEnvelope.status.manualEnableRequired",
- "等待成功运行",
+ "待试用",
),
source_metadata_ready: t(
"capabilityDraft.registeredPanel.agentEnvelope.status.sourceMetadataReady",
- "等待 Completion Audit",
+ "待检查",
),
},
widgetPending: t(
"capabilityDraft.registeredPanel.agentEnvelope.widget.pending",
- "Widget:等待运行后展示状态、产物、阻塞点和 evidence 入口。",
+ "结果:试用后展示最近状态和下一步。",
),
widgetReady: t(
"capabilityDraft.registeredPanel.agentEnvelope.widget.ready",
- "Widget:展示 Managed Job 状态、最近产物、审计结论和下一步动作。",
+ "结果:展示最近状态、产物和下一步动作。",
),
}),
[buildCompletionAuditLabel, t],
@@ -735,6 +819,70 @@ function WorkspaceRegisteredSkillCard({
managedAutomationCopy,
);
const [managedAutomationJob] = managedAutomationJobs;
+ const canAuditManagedAutomationJob = Boolean(
+ managedAutomationJob?.last_run_at ||
+ managedAutomationJob?.last_finished_at ||
+ managedAutomationJob?.last_status,
+ );
+ const userPermissionSummary = summarizePermissionForUser(skill, {
+ defaultReadonly: t(
+ "capabilityDraft.registeredPanel.user.permission.defaultReadonly",
+ "默认只读取信息",
+ ),
+ draftWrite: t(
+ "capabilityDraft.registeredPanel.user.permission.draftWrite",
+ "可写入草案",
+ ),
+ localCommand: t(
+ "capabilityDraft.registeredPanel.user.permission.localCommand",
+ "可运行本地命令",
+ ),
+ readonly: t(
+ "capabilityDraft.registeredPanel.user.permission.readonly",
+ "只读取信息",
+ ),
+ });
+ const userScheduleLabel = getUserScheduleLabel(managedAutomationJob, {
+ enabled: t(
+ "capabilityDraft.registeredPanel.user.schedule.enabled",
+ "已开启",
+ ),
+ notCreated: t(
+ "capabilityDraft.registeredPanel.user.schedule.notCreated",
+ "未设置",
+ ),
+ paused: t("capabilityDraft.registeredPanel.user.schedule.paused", "已暂停"),
+ });
+ const userResultLabel = getUserResultLabel(
+ managedAutomationJob,
+ completionAuditSummary,
+ {
+ auditBlocked: t(
+ "capabilityDraft.registeredPanel.user.result.auditBlocked",
+ "最近结果需要处理",
+ ),
+ auditPassed: t(
+ "capabilityDraft.registeredPanel.user.result.auditPassed",
+ "最近结果已通过检查",
+ ),
+ noJob: t(
+ "capabilityDraft.registeredPanel.user.result.noJob",
+ "还没有定时运行",
+ ),
+ noRun: t(
+ "capabilityDraft.registeredPanel.user.result.noRun",
+ "还没有运行记录",
+ ),
+ readyToCheck: t(
+ "capabilityDraft.registeredPanel.user.result.readyToCheck",
+ "可以检查最近结果",
+ ),
+ running: t(
+ "capabilityDraft.registeredPanel.user.result.running",
+ "正在运行",
+ ),
+ },
+ );
const managedAutomationUpdating =
managedAutomationJob?.id === managedAutomationUpdatingJobId;
const completionAuditAuditing =
@@ -769,11 +917,11 @@ function WorkspaceRegisteredSkillCard({
{bindingBlocked
? t(
"capabilityDraft.registeredPanel.card.binding.blocked",
- "Binding 阻塞",
+ "需要处理",
)
: t(
"capabilityDraft.registeredPanel.card.binding.candidate",
- "P3C binding 候选",
+ "可试用",
)}
@@ -789,617 +937,681 @@ function WorkspaceRegisteredSkillCard({
)}