Refresh skill prompts inside sandbox workspaces

This commit is contained in:
sunke 2026-03-21 10:49:08 +08:00
parent 6430c54f6f
commit cc0ef9e8a3
6 changed files with 67 additions and 12 deletions

View File

@ -520,18 +520,20 @@ export async function compactEmbeddedPiSessionDirect(
let restoreSkillEnv: (() => void) | undefined;
process.chdir(effectiveWorkspace);
try {
const preferWorkspaceSkillPaths = effectiveWorkspace !== resolvedWorkspace;
const { shouldLoadSkillEntries, skillEntries } = resolveEmbeddedRunSkillEntries({
workspaceDir: effectiveWorkspace,
config: params.config,
skillsSnapshot: params.skillsSnapshot,
preferWorkspaceEntries: preferWorkspaceSkillPaths,
});
restoreSkillEnv = params.skillsSnapshot
? applySkillEnvOverridesFromSnapshot({
snapshot: params.skillsSnapshot,
restoreSkillEnv = shouldLoadSkillEntries
? applySkillEnvOverrides({
skills: skillEntries ?? [],
config: params.config,
})
: applySkillEnvOverrides({
skills: skillEntries ?? [],
: applySkillEnvOverridesFromSnapshot({
snapshot: params.skillsSnapshot,
config: params.config,
});
const skillsPrompt = resolveSkillsPromptForRun({
@ -539,6 +541,7 @@ export async function compactEmbeddedPiSessionDirect(
entries: shouldLoadSkillEntries ? skillEntries : undefined,
config: params.config,
workspaceDir: effectiveWorkspace,
preferEntries: preferWorkspaceSkillPaths,
});
const sessionLabel = params.sessionKey ?? params.sessionId;

View File

@ -1423,18 +1423,20 @@ export async function runEmbeddedAttempt(
let restoreSkillEnv: (() => void) | undefined;
process.chdir(effectiveWorkspace);
try {
const preferWorkspaceSkillPaths = effectiveWorkspace !== resolvedWorkspace;
const { shouldLoadSkillEntries, skillEntries } = resolveEmbeddedRunSkillEntries({
workspaceDir: effectiveWorkspace,
config: params.config,
skillsSnapshot: params.skillsSnapshot,
preferWorkspaceEntries: preferWorkspaceSkillPaths,
});
restoreSkillEnv = params.skillsSnapshot
? applySkillEnvOverridesFromSnapshot({
snapshot: params.skillsSnapshot,
restoreSkillEnv = shouldLoadSkillEntries
? applySkillEnvOverrides({
skills: skillEntries ?? [],
config: params.config,
})
: applySkillEnvOverrides({
skills: skillEntries ?? [],
: applySkillEnvOverridesFromSnapshot({
snapshot: params.skillsSnapshot,
config: params.config,
});
@ -1443,6 +1445,7 @@ export async function runEmbeddedAttempt(
entries: shouldLoadSkillEntries ? skillEntries : undefined,
config: params.config,
workspaceDir: effectiveWorkspace,
preferEntries: preferWorkspaceSkillPaths,
});
const sessionLabel = params.sessionKey ?? params.sessionId;

View File

@ -67,4 +67,25 @@ describe("resolveEmbeddedRunSkillEntries", () => {
});
expect(hoisted.loadWorkspaceSkillEntries).not.toHaveBeenCalled();
});
it("reloads skill entries when sandbox execution prefers workspace-local skill paths", () => {
const snapshot: SkillSnapshot = {
prompt: "skills prompt",
skills: [{ name: "diffs" }],
resolvedSkills: [],
};
const result = resolveEmbeddedRunSkillEntries({
workspaceDir: "/tmp/workspace-sandbox",
config: {},
skillsSnapshot: snapshot,
preferWorkspaceEntries: true,
});
expect(result.shouldLoadSkillEntries).toBe(true);
expect(hoisted.loadWorkspaceSkillEntries).toHaveBeenCalledTimes(1);
expect(hoisted.loadWorkspaceSkillEntries).toHaveBeenCalledWith("/tmp/workspace-sandbox", {
config: {},
});
});
});

View File

@ -5,11 +5,15 @@ export function resolveEmbeddedRunSkillEntries(params: {
workspaceDir: string;
config?: OpenClawConfig;
skillsSnapshot?: SkillSnapshot;
preferWorkspaceEntries?: boolean;
}): {
shouldLoadSkillEntries: boolean;
skillEntries: SkillEntry[];
} {
const shouldLoadSkillEntries = !params.skillsSnapshot || !params.skillsSnapshot.resolvedSkills;
const shouldLoadSkillEntries =
params.preferWorkspaceEntries ||
!params.skillsSnapshot ||
!params.skillsSnapshot.resolvedSkills;
return {
shouldLoadSkillEntries,
skillEntries: shouldLoadSkillEntries

View File

@ -29,4 +29,27 @@ describe("resolveSkillsPromptForRun", () => {
expect(prompt).toContain("<available_skills>");
expect(prompt).toContain("/app/skills/demo-skill/SKILL.md");
});
it("prefers workspace-built prompt when sandbox execution overrides snapshot paths", () => {
const entry: SkillEntry = {
skill: {
name: "demo-skill",
description: "Demo",
filePath: "/sandbox/workspace/skills/demo-skill/SKILL.md",
baseDir: "/sandbox/workspace/skills/demo-skill",
source: "workspace",
disableModelInvocation: false,
},
frontmatter: {},
};
const prompt = resolveSkillsPromptForRun({
skillsSnapshot: { prompt: "SNAPSHOT", skills: [] },
entries: [entry],
workspaceDir: "/sandbox/workspace",
preferEntries: true,
});
expect(prompt).toContain("/sandbox/workspace/skills/demo-skill/SKILL.md");
expect(prompt).not.toBe("SNAPSHOT");
});
});

View File

@ -696,9 +696,10 @@ export function resolveSkillsPromptForRun(params: {
entries?: SkillEntry[];
config?: OpenClawConfig;
workspaceDir: string;
preferEntries?: boolean;
}): string {
const snapshotPrompt = params.skillsSnapshot?.prompt?.trim();
if (snapshotPrompt) {
if (snapshotPrompt && !params.preferEntries) {
return snapshotPrompt;
}
if (params.entries && params.entries.length > 0) {