mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-05 06:31:42 +00:00
fix(skills): avoid recursive workshop recovery locks
This commit is contained in:
@@ -83,7 +83,10 @@ export type SkillProposalApplyTransitionDependencies = {
|
||||
workspaceDir?: string,
|
||||
env?: NodeJS.ProcessEnv,
|
||||
agentId?: string,
|
||||
config?: OpenClawConfig,
|
||||
readOptions?: {
|
||||
config?: OpenClawConfig;
|
||||
reconcile?: boolean;
|
||||
},
|
||||
) => Promise<SkillProposalReadResult>;
|
||||
};
|
||||
|
||||
@@ -113,12 +116,17 @@ export async function applySkillProposalTransition(
|
||||
input: SkillProposalActionInput,
|
||||
dependencies: SkillProposalApplyTransitionDependencies,
|
||||
): Promise<SkillProposalApplyResult> {
|
||||
const recoveryReadOptions = input.config ? { config: input.config } : undefined;
|
||||
const lockedReadOptions = {
|
||||
...(input.config ? { config: input.config } : {}),
|
||||
reconcile: false,
|
||||
};
|
||||
const initial = await dependencies.readRequiredProposal(
|
||||
input.proposalId,
|
||||
input.workspaceDir,
|
||||
input.env,
|
||||
input.agentId,
|
||||
input.config,
|
||||
recoveryReadOptions,
|
||||
);
|
||||
if (initial.record.status !== "pending") {
|
||||
throw new Error(
|
||||
@@ -149,7 +157,7 @@ export async function applySkillProposalTransition(
|
||||
input.workspaceDir,
|
||||
input.env,
|
||||
input.agentId,
|
||||
input.config,
|
||||
lockedReadOptions,
|
||||
);
|
||||
if (
|
||||
current.record.status === "pending" &&
|
||||
@@ -190,7 +198,7 @@ export async function applySkillProposalTransition(
|
||||
input.workspaceDir,
|
||||
input.env,
|
||||
input.agentId,
|
||||
input.config,
|
||||
lockedReadOptions,
|
||||
);
|
||||
const { record, content } = read;
|
||||
if (record.status !== "pending") {
|
||||
|
||||
@@ -57,6 +57,7 @@ export async function evaluateSkillProposal(
|
||||
input.workspaceDir,
|
||||
input.env,
|
||||
input.agentId,
|
||||
{ reconcile: false },
|
||||
);
|
||||
if (read.record.status !== "pending") {
|
||||
throw new Error(
|
||||
@@ -154,6 +155,7 @@ export async function evaluateSkillProposal(
|
||||
input.workspaceDir,
|
||||
input.env,
|
||||
input.agentId,
|
||||
{ reconcile: false },
|
||||
);
|
||||
if (
|
||||
current.record.status !== "pending" ||
|
||||
|
||||
@@ -16,6 +16,11 @@ type SkillProposalScopeOptions = {
|
||||
workspaceDir?: string;
|
||||
};
|
||||
|
||||
type RequiredProposalReadOptions = {
|
||||
config?: OpenClawConfig;
|
||||
reconcile?: boolean;
|
||||
};
|
||||
|
||||
function storeOptions(env?: NodeJS.ProcessEnv) {
|
||||
return env ? { env } : {};
|
||||
}
|
||||
@@ -123,7 +128,7 @@ export async function readRequiredProposal(
|
||||
workspaceDir?: string,
|
||||
env?: NodeJS.ProcessEnv,
|
||||
agentId?: string,
|
||||
config?: OpenClawConfig,
|
||||
readOptions: RequiredProposalReadOptions = {},
|
||||
): Promise<SkillProposalReadResult> {
|
||||
const read = await readSkillProposal(
|
||||
proposalId,
|
||||
@@ -132,7 +137,7 @@ export async function readRequiredProposal(
|
||||
...(agentId ? { agentId } : {}),
|
||||
...(workspaceDir ? { workspaceDir } : {}),
|
||||
},
|
||||
config,
|
||||
readOptions,
|
||||
);
|
||||
if (!read) {
|
||||
throw new Error(`Skill proposal not found: ${proposalId}`);
|
||||
|
||||
@@ -1159,6 +1159,16 @@ describe("skill workshop proposals", () => {
|
||||
"# External change\n",
|
||||
);
|
||||
await expect(readSkillProposalRollback(proposal.record.id)).resolves.not.toBeNull();
|
||||
|
||||
const startedAt = Date.now();
|
||||
await expect(
|
||||
rejectSkillProposal({
|
||||
workspaceDir,
|
||||
proposalId: proposal.record.id,
|
||||
reason: "external target retained",
|
||||
}),
|
||||
).resolves.toMatchObject({ status: "rejected" });
|
||||
expect(Date.now() - startedAt).toBeLessThan(2_000);
|
||||
});
|
||||
|
||||
it("reconciles an update apply interrupted after the live skill write", async () => {
|
||||
|
||||
@@ -642,12 +642,17 @@ async function withPendingSkillProposalMutation<T>(
|
||||
action: "applied" | "quarantined" | "rejected" | "revised",
|
||||
fn: (read: SkillProposalReadResult) => Promise<T>,
|
||||
): Promise<T> {
|
||||
const recoveryReadOptions = input.config ? { config: input.config } : undefined;
|
||||
const lockedReadOptions = {
|
||||
...(input.config ? { config: input.config } : {}),
|
||||
reconcile: false,
|
||||
};
|
||||
const initial = await readRequiredProposal(
|
||||
input.proposalId,
|
||||
input.workspaceDir,
|
||||
input.env,
|
||||
input.agentId,
|
||||
input.config,
|
||||
recoveryReadOptions,
|
||||
);
|
||||
return await withSkillProposalTargetLock(
|
||||
initial.record,
|
||||
@@ -657,7 +662,7 @@ async function withPendingSkillProposalMutation<T>(
|
||||
input.workspaceDir,
|
||||
input.env,
|
||||
input.agentId,
|
||||
input.config,
|
||||
lockedReadOptions,
|
||||
);
|
||||
if (read.record.status !== "pending") {
|
||||
throw new Error(
|
||||
|
||||
@@ -71,6 +71,11 @@ type SkillProposalLookupScope = {
|
||||
workspaceDir?: string;
|
||||
};
|
||||
|
||||
type SkillProposalReadOptions = {
|
||||
config?: OpenClawConfig;
|
||||
reconcile?: boolean;
|
||||
};
|
||||
|
||||
export type PreparedSkillProposalSupportFile = SkillProposalSupportFile & {
|
||||
content: string;
|
||||
};
|
||||
@@ -178,13 +183,15 @@ export async function readSkillProposal(
|
||||
proposalId: string,
|
||||
options: SkillWorkshopStoreOptions = {},
|
||||
scope: SkillProposalLookupScope = {},
|
||||
recoveryConfig?: OpenClawConfig,
|
||||
readOptions: SkillProposalReadOptions = {},
|
||||
): Promise<SkillProposalReadResult | null> {
|
||||
let stored = readStoredProposal(proposalId, options);
|
||||
if (!stored || !isStoredProposalVisible(stored.row, scope)) {
|
||||
return null;
|
||||
}
|
||||
await reconcileInterruptedApply(proposalId, options, recoveryConfig);
|
||||
if (readOptions.reconcile !== false) {
|
||||
await reconcileInterruptedApply(proposalId, options, readOptions.config);
|
||||
}
|
||||
stored = readStoredProposal(proposalId, options);
|
||||
if (!stored || !isStoredProposalVisible(stored.row, scope)) {
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user