























@@ -2,6 +2,7 @@ import fs from "node:fs/promises";
22import os from "node:os";
33import path from "node:path";
44import type { AnyAgentTool } from "openclaw/plugin-sdk/agent-runtime";
5+import type { PluginTrustedToolPolicyRegistration } from "openclaw/plugin-sdk/core";
56import { createTestPluginApi } from "openclaw/plugin-sdk/plugin-test-api";
67import { afterEach, describe, expect, it, vi } from "vitest";
78import plugin, {
@@ -617,6 +618,72 @@ describe("skill-workshop", () => {
617618expect(await store.list("pending")).toHaveLength(1);
618619});
619620621+it("queues apply true suggestions in pending mode before explicit apply", async () => {
622+const workspaceDir = await makeTempDir();
623+const stateDir = await makeTempDir();
624+let tool: AnyAgentTool | undefined;
625+const api = createTestPluginApi({
626+pluginConfig: { approvalPolicy: "pending" },
627+runtime: {
628+agent: {
629+resolveAgentWorkspaceDir: () => workspaceDir,
630+},
631+state: {
632+resolveStateDir: () => stateDir,
633+},
634+} as never,
635+registerTool(registered) {
636+const resolved =
637+typeof registered === "function" ? registered({ workspaceDir }) : registered;
638+tool = Array.isArray(resolved) ? resolved[0] : (resolved ?? undefined);
639+},
640+});
641+642+plugin.register(api);
643+const result = await tool?.execute?.("call-1", {
644+action: "suggest",
645+apply: true,
646+skillName: "screenshot-asset-workflow",
647+description: "Screenshot asset workflow",
648+body: "Verify dimensions, optimize the PNG, and run the relevant gate.",
649+});
650+651+expect(result?.details).toMatchObject({ status: "pending" });
652+const proposalId =
653+(result?.details as { proposal?: { id?: string } } | undefined)?.proposal?.id ?? "";
654+expect(proposalId).toBeTruthy();
655+await expect(
656+fs.access(path.join(workspaceDir, "skills", "screenshot-asset-workflow", "SKILL.md")),
657+).rejects.toMatchObject({ code: "ENOENT" });
658+const store = new SkillWorkshopStore({ stateDir, workspaceDir });
659+expect(await store.list("pending")).toHaveLength(1);
660+expect(await store.list("applied")).toHaveLength(0);
661+});
662+663+it("requires operator approval before applying queued proposals in pending mode", async () => {
664+let trustedPolicy: PluginTrustedToolPolicyRegistration | undefined;
665+const api = createTestPluginApi({
666+pluginConfig: { approvalPolicy: "pending" },
667+registerTrustedToolPolicy(policy) {
668+trustedPolicy = policy;
669+},
670+});
671+672+plugin.register(api);
673+674+const result = await trustedPolicy?.evaluate(
675+{ toolName: "skill_workshop", params: { action: "apply", id: "proposal-1" } },
676+{ toolName: "skill_workshop" },
677+);
678+679+expect(result).toMatchObject({
680+requireApproval: {
681+title: "Apply workspace skill proposal",
682+allowedDecisions: ["allow-once", "deny"],
683+},
684+});
685+});
686+620687it("uses the reviewer to propose existing skill repairs", async () => {
621688const workspaceDir = await makeTempDir();
622689const stateDir = await makeTempDir();
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。